package com.xlh.service.course.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xlh.constant.UserConstant;
import com.xlh.dao.course.*;
import com.xlh.dao.course.ext.ChapterCourseSortMapperExt;
import com.xlh.dao.course.ext.ChapterMapperExt;
import com.xlh.dao.course.ext.NotesMapperExt;
import com.xlh.dao.course.ext.ResourceAuthorityMapperExt;
import com.xlh.dto.course.*;
import com.xlh.dto.system.OperationDTO;
import com.xlh.dto.system.ResourceRelationDTO;
import com.xlh.dto.user.ClassDTO;
import com.xlh.dto.user.UserInfoDTO;
import com.xlh.enums.*;
import com.xlh.enums.course.*;
import com.xlh.enums.courseTest.PaperStatusEnum;
import com.xlh.enums.courseTest.QuestionBusinessTypeEnum;
import com.xlh.enums.courseTest.QuestionOriginEnum;
import com.xlh.enums.user.EventCodeEnum;
import com.xlh.enums.user.IdentityEnum;
import com.xlh.enums.user.RoleEnum;
import com.xlh.exception.DownloadFileException;
import com.xlh.exception.common.BadRequestException;
import com.xlh.exception.common.GlobalException;
import com.xlh.exception.common.NotFoundException;
import com.xlh.listener.AsyncTask;
import com.xlh.param.course.*;
import com.xlh.pojo.course.*;
import com.xlh.pojo.system.*;
import com.xlh.pojo.user.Class;
import com.xlh.pojo.user.User;
import com.xlh.rest.request.ConvertFileRequest;
import com.xlh.rest.response.ConvertFileResponse;
import com.xlh.security.UserInfoHolder;
import com.xlh.service.MdHtmlFileService;
import com.xlh.service.course.*;
import com.xlh.service.courseTest.PaperAnswerSheetService;
import com.xlh.service.courseTest.PracticeService;
import com.xlh.service.courseTest.QuestionRepositoryService;
import com.xlh.service.system.OperationService;
import com.xlh.service.system.SystemConfigService;
import com.xlh.service.system.SystemResourceService;
import com.xlh.service.user.ClassService;
import com.xlh.service.user.UserService;
import com.xlh.support.quiz.service.QuizManageService;
import com.xlh.util.BeanUtil;
import com.xlh.virtualization.constant.ContextTypeEnum;
import com.xlh.virtualization.service.ContainerService;
import com.xlh.virtualization.service.ExperimentJudgeService;
import com.xlh.virtualization.service.ImageAllocationService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 课程
 * <p>
 * Created by lx on 2019/1/28.
 */
@Service
public class ChapterServiceImpl implements ChapterService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ChapterServiceImpl.class);

    @Autowired
    private ChapterMapper chapterMapper;

    @Autowired
    private ChapterBasicMapper chapterBasicMapper;

    @Autowired
    private ChapterMapperExt chapterMapperExt;

    @Autowired
    private ChapterCourseSortMapper chapterCourseSortMapper;

    @Autowired
    private ChapterCourseSortMapperExt chapterCourseSortMapperExt;

    @Autowired
    private ResourceAuthorityMapper resourceAuthorityMapper;

    @Autowired
    private ResourceAuthorityMapperExt resourceAuthorityMapperExt;

    @Autowired
    private TaskService taskService;

    @Autowired
    private OperationService operationService;

    @Autowired
    private CourseService courseService;

    @Autowired
    private UserService userService;

    @Autowired
    private ClassService classService;

    @Autowired
    private NotesMapper notesMapper;

    @Autowired
    private NotesMapperExt notesMapperExt;

    @Autowired
    private UserInfoHolder userInfoHolder;

    @Autowired
    private AsyncTask asyncTask;

    @Autowired
    private MdHtmlFileService mdHtmlFileService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private PracticeService practiceService;

    @Autowired
    private ContainerService containerService;

    @Autowired
    private ImageAllocationService imageAllocationService;

    @Autowired
    private QuizManageService quizManageService;

    @Autowired
    private com.xlh.service.courseTest.ExperimentService experimentService;

    @Autowired
    private QuestionRepositoryService questionRepositoryService;

    @Autowired
    private TechnologyService technologyService;

    @Autowired
    private SystemResourceService resourceService;

    @Value("#{'http://' + '${server.ip}' + ':' + '${project.convert-project-port}'}")
    private String convertProjectUrl;

    @Autowired
    private PaperAnswerSheetService paperAnswerSheetService;

    @Autowired
    private SystemConfigService configService;

    @Autowired
    private DirectionService directionService;

    @Autowired
    private StepService stepService;

    @Autowired
    private ExperimentJudgeService experimentJudgeService;

    @Override
    public List<Chapter> getChapter(Long courseId, ChapterEnum chapterEnum) {
        ChapterExample example = new ChapterExample();
        example.setOrderByClause("order_number ASC");
        example.createCriteria().andCourseIdEqualTo(courseId).andDeletedEqualTo(false);
        List<Chapter> data = chapterMapper.selectByExample(example);
        List<ChapterBasicInfoDTO> chapters = BeanUtil.batchTransform(ChapterBasicInfoDTO.class, data);
        return BeanUtil.batchTransform(
                Chapter.class, chapterEnum == null ? chapters : chapterEnum.getConvertData(chapters));
    }

    @Override
    public List<ChapterBasic> getChapterBasic(
            Long courseId, List<Long> chapterIds, List<Integer> types, ChapterEnum chapterEnum) {
        List<Long> courseIds = courseId == null ? null : Lists.newArrayList(courseId);
        List<ChapterBasicInfoDTO> chapterBasics = listChapterInfo(courseIds, chapterIds, types);
        chapterBasics = chapterEnum == null ? chapterBasics : chapterEnum.getConvertData(chapterBasics);
        if (CollectionUtils.isEmpty(chapterBasics)) throw new NotFoundException("暂无章节信息");

        if (chapterEnum == null) {
            List<ChapterBasicInfoDTO> chapters = ChapterEnum.CHAPTER.getConvertData(chapterBasics);
            List<ChapterBasicInfoDTO> tasks = ChapterEnum.TASK.getConvertData(chapterBasics);
            chapterBasics = chapters;
            chapterBasics.addAll(tasks);
        }

        return BeanUtil.batchTransform(ChapterBasic.class, chapterBasics);
    }

    @Override
    public List<ChapterBasicInfoDTO> listChapter(
            List<Long> courseIds, List<Long> ids, List<Integer> types, ChapterEnum chapterEnum) {
        List<ChapterBasicInfoDTO> data = listChapterInfo(courseIds, ids, types);
        return chapterEnum == null ? data : chapterEnum.getConvertData(data);
    }

    @Override
    public List<ChapterBasicInfoDTO> listChapterInfo(List<Long> courseIds, List<Long> ids, List<Integer> types) {
        return chapterMapperExt.listChapter(courseIds, ids, types);
    }

    @Override
    public List<ChapterBasicInfoDTO> listTask(List<Long> parentIds, String info) {
        return chapterMapperExt.listTask(parentIds, info);
    }

    @Override
    public List<ChapterBasicInfoDTO> listTask(List<Long> courseIds, List<Integer> types, Boolean flag) {

        List<ChapterBasicInfoDTO> chapterData = listChapterInfo(courseIds, null, types);
        List<ChapterBasicInfoDTO> taskChapters = ChapterEnum.TASK.getConvertData(chapterData);
        List<ChapterBasicInfoDTO> chapters = ChapterEnum.CHAPTER.getConvertData(chapterData);

        List<ChapterBasicInfoDTO> tasks = Lists.newArrayList();
        for (ChapterBasicInfoDTO chapter : chapters) {
            if (flag && !Objects.equals(chapter.getFalg(), 0)) {
                continue;
            }
            List<ChapterBasicInfoDTO> data = taskChapters.stream()
                    .filter(chapterBasicInfoDTO -> Objects.equals(chapterBasicInfoDTO.getParentId(), chapter.getId()))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(data)) continue;
            tasks.addAll(data);
        }

        return tasks;
    }

    @Override
    public List<ChapterBasicInfoDTO> queryChapterTechnology(List<Long> courseIds) {
        List<ChapterBasicInfoDTO> chapterBasics = listChapterInfo(courseIds, null, null);
        if (CollectionUtils.isEmpty(chapterBasics)) {
            return null;
        }
        return chapterBasics;
    }

    @Override
    public List<ChapterBasicDTO> listChapter(Long courseId) {

        List<Long> courseIds = courseId == null ? null : Lists.newArrayList(courseId);
        List<ChapterBasicInfoDTO> chapterBasics = listChapterInfo(courseIds, null, null);
        if (CollectionUtils.isEmpty(chapterBasics)) throw new NotFoundException("暂无章节信息");

        // 转换并构造结果集
        List<ChapterBasicDTO> data = convert(userInfoHolder, chapterBasics, courseId);
        return data;
    }

    @Override
    public List<ChapterBasicInfoDTO> listChapterInfo(Long courseId) {
        // 获取章节基本信息
        List<ChapterBasicInfoDTO> chapterInfos = listChapter(
                Lists.newArrayList(courseId), null, null, ChapterEnum.CHAPTER);

        // 获取课程分类数据
        List<CourseSort> courseSorts = courseService.queryCourseSort(Lists.newArrayList(courseId));
        List<Long> courseSortIds = courseSorts.stream().map(CourseSort::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(courseSortIds)) return chapterInfos;

        // 获取章节与课程分类的关联关系
        List<ChapterCourseSort> chapterCourseSorts = queryChapterCourseSort(courseSortIds, null);
        List<Long> chapterIds = chapterCourseSorts.stream()
                .map(ChapterCourseSort::getChapterId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(chapterIds)) return chapterInfos;

        // 转换数据
        chapterInfos.forEach(chapterBasicDTO -> {
            chapterBasicDTO.setContainCourseSortFlag(chapterIds.contains(chapterBasicDTO.getId()));
        });
        return chapterInfos;
    }

    @Override
    public List<ChapterBasicDTO> convertChapterBasic(
            ChapterConvertTypeEnum chapterConvertTypeEnum, List<ChapterBasicDTO> data,
            UserCourseStatusEnum userCourseStatusEnum, List<Long> studiedChapterIds,
            Map<Long, Boolean> practiceExistMap, Map<Long, PaperStatusEnum> chapterPracticeMap,
            List<ChapterBasicDTO> courseSortData, OperationDTO operationDTO,
            List<SchoolTypeEnum> courseSchoolTypeEnums) {

        // 获取章节id集合
        List<Long> chapterIds = data.stream().map(ChapterBasicDTO::getId).distinct().collect(Collectors.toList());

        // resource_relation 和 resource 关联获取课程中的全部资源 并将结果集转换为Map<Long, List<ResourceRelationDTO>>
        List<ChapterBasicInfoDTO> tasks = listTask(chapterIds, null);
        List<Long> taskIds = tasks.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
        taskIds.addAll(chapterIds);
        List<ResourceRelationDTO> resourceData = resourceService.getResourceRelationInfo(
                taskIds, null, null, ResourceRelationTypeEnum.CHAPTER.getCode());
        Map<Long, List<ResourceRelationDTO>> chapterIdMap = BeanUtil.aggByKeyToList("objId", resourceData);

        List<ChapterBasicInfoDTO> chapterCourseInfos = listChapterInfo(null, chapterIds, null);
        Map<Long, ChapterBasicInfoDTO> chapterCourseMap = BeanUtil.mapByKey("id", chapterCourseInfos);

        // 学生是否考试
        List<Long> courseIds = data.stream().map(ChapterBasicDTO::getCourseId).collect(Collectors.toList());
        Map<Long, Boolean> studentIsTestingMap = Maps.newHashMap();
        if (chapterConvertTypeEnum == ChapterConvertTypeEnum.CHAPTER) {
            studentIsTestingMap = courseService.getStudentIsTesting(courseIds);
        }

        // 大数据技术库中，学生只要考试，就将技术库中所有章节都锁上
        if (chapterConvertTypeEnum == ChapterConvertTypeEnum.TECHNOLOGY
                && userInfoHolder.isStudent()
                && CollectionUtils.isNotEmpty(paperAnswerSheetService.getTestingCourse(
                userInfoHolder.getStudentClassIds()))) {
            for (Long courseId : courseIds) {
                studentIsTestingMap.put(courseId, true);
            }
        }

        // 配置章节分类的数据
        if (CollectionUtils.isNotEmpty(courseSortData)) {

            for (ChapterBasicDTO courseSortChapterData : courseSortData) {

                // 将课程分类的章节进行排序并获取
                List<ChapterBasicDTO> dataInfo = convert(data, courseSortChapterData.getChapterIds());

                // 设置章节的信息
                List<ChapterBasicDTO> chapterBasicDTOS = setChapterInfo(dataInfo, chapterCourseMap, studiedChapterIds,
                        chapterIdMap, studentIsTestingMap, userCourseStatusEnum, practiceExistMap,
                        chapterPracticeMap, null, operationDTO, courseSchoolTypeEnums);
                courseSortChapterData.setChildren(chapterBasicDTOS);

                // 设置已学完
                courseSortChapterData.setStudied(
                        CollectionUtils.isNotEmpty(chapterBasicDTOS) && chapterBasicDTOS.stream()
                                .filter(chapterBasicDTO -> !chapterBasicDTO.getStudied()).count() == 0 ? true : false);

                // 设置最近学习
                if (operationDTO != null) {
                    setChapterRecentlyStudiedFlag(courseSortChapterData);
                }

                // 将data数据进行过滤
                List<Long> useChapterIds = dataInfo.stream().map(ChapterBasicDTO::getId).collect(Collectors.toList());
                data = data.stream()
                        .filter(chapterBasicDTO -> !useChapterIds.contains(chapterBasicDTO.getId()))
                        .collect(Collectors.toList());

                // 子集加本级的章节进行存放。-> 为了前端实现正在学习
                courseSortChapterData.setChapterIds(useChapterIds);
            }

            courseSortData = courseSortData.stream()
                    .filter(chapterBasicDTO -> CollectionUtils.isNotEmpty(chapterBasicDTO.getChildren()))
                    .collect(Collectors.toList());

        }

        // 设置章节的信息
        List<ChapterBasicDTO> chapterBasicDTOS = setChapterInfo(
                data, chapterCourseMap, studiedChapterIds, chapterIdMap,
                studentIsTestingMap, userCourseStatusEnum, practiceExistMap,
                chapterPracticeMap, 2, operationDTO, courseSchoolTypeEnums);

        courseSortData = courseSortData == null ? Lists.newArrayList() : courseSortData;
        courseSortData.addAll(chapterBasicDTOS);

        return courseSortData;
    }

    private void setChapterRecentlyStudiedFlag(ChapterBasicDTO courseSortChapterData) {
        for (ChapterBasicDTO data : courseSortChapterData.getChildren()) {
            if (data.getRecentlyStudiedFlag() > 0) {
                courseSortChapterData.setRecentlyStudiedFlag(data.getRecentlyStudiedFlag());
                break;
            }
        }
    }

    private boolean studied(
            List<Long> childrenChapterIds, Long parentChapterId, Integer chapterType, List<Long> studiedChapterIds) {
        List<Long> allChapterIds = childrenChapterIds == null ? Lists.newArrayList() : childrenChapterIds;
        if (chapterType != null && parentChapterId != null
                && ChapterTypeEnum.isContainsTheory(chapterType)) {
            allChapterIds.add(parentChapterId);
        }
        if (CollectionUtils.isNotEmpty(studiedChapterIds)
                && CollectionUtils.isNotEmpty(allChapterIds)
                && allChapterIds.stream()
                .filter(chapterId -> !studiedChapterIds.contains(chapterId)).count() == 0) {
            return true;
        }
        return false;
    }

    private List<ChapterBasicDTO> convert(List<ChapterBasicDTO> data, List<Long> chapterIds) {
        List<ChapterBasicDTO> result = Lists.newArrayList();
        Map<Long, ChapterBasicDTO> chapterMap = BeanUtil.mapByKey("id", data);
        Map<Long, List<ChapterBasicDTO>> childrenChapterMap = BeanUtil.aggByKeyToList("parentId", data);
        chapterIds.forEach(chapterId -> {
            if (MapUtils.isEmpty(chapterMap) || chapterMap.get(chapterId) == null) {
                return;
            }
            result.add(chapterMap.get(chapterId));
            if (MapUtils.isEmpty(childrenChapterMap) || CollectionUtils.isEmpty(childrenChapterMap.get(chapterId))) {
                return;
            }
            // 将子节点的章节进行排序
            result.addAll(childrenChapterMap.get(chapterId).stream()
                    .sorted(Comparator.comparing(ChapterBasicDTO::getOrderNumber))
                    .collect(Collectors.toList()));

        });
        return result;
    }

    private List<ChapterBasicDTO> setChapterInfo(
            List<ChapterBasicDTO> data, Map<Long, ChapterBasicInfoDTO> chapterCourseMap,
            List<Long> studiedChapterIds, Map<Long, List<ResourceRelationDTO>> chapterIdMap,
            Map<Long, Boolean> studentIsTestingMap, UserCourseStatusEnum userCourseStatusEnum,
            Map<Long, Boolean> practiceExistMap, Map<Long, PaperStatusEnum> chapterPracticeMap,
            Integer levels, OperationDTO operationDTO, List<SchoolTypeEnum> courseSchoolTypeEnums) {

        // 子集章节集合
        List<ChapterBasicDTO> chapters = data.stream()
                .filter(chapterBasicDTO -> chapterBasicDTO.getParentId() != null && chapterBasicDTO.getParentId() > 0)
                .collect(Collectors.toList());
        Map<Long, List<ChapterBasicDTO>> childrenChapterMap = BeanUtil.aggByKeyToList("parentId", chapters);

        // 父级章节id集合
        List<ChapterBasicDTO> parentChapterIds = data.stream()
                .filter(chapterBasicDTO -> chapterBasicDTO.getParentId() == null || chapterBasicDTO.getParentId() == 0)
                .collect(Collectors.toList());

        for (ChapterBasicDTO chapterBasicDTO : parentChapterIds) {

            // 设置层级数量
            chapterBasicDTO.setLevels(levels);

            // 设置课程id
            chapterBasicDTO.setCourseId(chapterCourseMap.get(chapterBasicDTO.getId()).getCourseId());

            // 设置章节的类型
            chapterBasicDTO.setTypeName(ChapterTypeEnum.getMessageByCode(chapterBasicDTO.getType()));

            // 设置已学完
            List<ChapterBasicDTO> childrenChapters = childrenChapterMap.get(chapterBasicDTO.getId());
            List<Long> childrenChapterIds = CollectionUtils.isEmpty(childrenChapters) ?
                    Lists.newArrayList() : childrenChapters.stream()
                    .map(ChapterBasicDTO::getId).collect(Collectors.toList());
            chapterBasicDTO.setStudied(
                    studied(childrenChapterIds, chapterBasicDTO.getId(), chapterBasicDTO.getType(), studiedChapterIds));

            // 获取章节类型的枚举
            ChapterTypeEnum chapterTypeEnum = ChapterTypeEnum.getByCode(chapterBasicDTO.getType());
            if (chapterTypeEnum == null) continue;

            // 获取资源
            List<ResourceRelationDTO> resourceDTOS = MapUtils.isEmpty(chapterIdMap)
                    || CollectionUtils.isEmpty(chapterIdMap.get(chapterBasicDTO.getId())) ?
                    Lists.newArrayList() : chapterIdMap.get(chapterBasicDTO.getId());
            childrenChapterIds.forEach(childChapterId -> {
                if (MapUtils.isEmpty(chapterIdMap) || CollectionUtils.isEmpty(chapterIdMap.get(childChapterId))) {
                    return;
                }
                resourceDTOS.addAll(chapterIdMap.get(childChapterId));
            });

            // 学生是否考试
            boolean studentIsTesting = MapUtils.isNotEmpty(studentIsTestingMap)
                    && studentIsTestingMap.get(chapterBasicDTO.getCourseId()) != null ?
                    studentIsTestingMap.get(chapterBasicDTO.getCourseId()) : false;

            // 其他信息
            List<ChapterDTO> otherInfo = convertChapterOtherInfo(chapterBasicDTO, chapterTypeEnum,
                    resourceDTOS, userCourseStatusEnum, practiceExistMap, chapterPracticeMap,
                    studentIsTesting, childrenChapterMap, operationDTO, courseSchoolTypeEnums);

            // 设置实验课程标识
            chapterBasicDTO.setExperimentCourseFlag(
                    ChapterTypeEnum.isContainsExperiment(chapterBasicDTO.getType())
            );

            // 将章节其他信息都放到结果集中
            chapterBasicDTO.setOtherInfo(otherInfo);

            // 设置已学习
            List<Long> chapterIds = otherInfo.stream().map(ChapterDTO::getId).collect(Collectors.toList());
            if (operationDTO != null && chapterIds.contains(operationDTO.getId())) {
                chapterBasicDTO.setRecentlyStudiedFlag(1);
            }

            // 子集加本级的章节进行存放。-> 为了前端实现正在学习
            List<Long> chapterIdData = Lists.newArrayList(chapterBasicDTO.getId());
            chapterIdData.addAll(childrenChapterIds);
            chapterBasicDTO.setChapterIds(chapterIdData);

        }

        return parentChapterIds;

    }

    @Override
    public List<ChapterBasicInfoDTO> listChapter(List<Long> courseIds) {
        List<ChapterBasicInfoDTO> chapterBasics = listChapter(courseIds, null, null, null);
        if (CollectionUtils.isEmpty(chapterBasics)
                || CollectionUtils.isEmpty(ChapterEnum.CHAPTER.getConvertData(chapterBasics))) {
            throw new NotFoundException("暂无章节信息");
        }
        return convertChapterBasicInfoDTO(chapterBasics);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateResourceAuthority(
            Long courseId, List<UpdateResourceAuthorityParam> params, AuthorityOperatingTypeEnum typeEnum) {

        List<Long> chapterIds = null;
        if (typeEnum == AuthorityOperatingTypeEnum.CHAPTER) {
            Map<Long, List<UpdateResourceAuthorityParam>> chapterIdMap = BeanUtil.aggByKeyToList(
                    "chapterId", params);
            chapterIds = Lists.newArrayList(chapterIdMap.keySet());
        }

        // 检查参数
        params = checkAndGetParam(courseId, chapterIds, params, typeEnum);

        // 转换参数
        List<Long> objIds = typeEnum == AuthorityOperatingTypeEnum.COURSE ? Lists.newArrayList(courseId) : chapterIds;
        Long userId = courseService.getCourseById(courseId).getUserId();
        List<ResourceAuthority> resourceAuthorities = convertResourceAuthorityList(objIds, userId, params, typeEnum);

        UpdateResourceRightTypeEnum rightTypeEnum = UpdateResourceRightTypeEnum.ALL;
        if (resourceAuthorities.stream()
                .filter(resourceAuthority -> resourceAuthority.getDownloaded() != null).count() > 0) {
            rightTypeEnum = UpdateResourceRightTypeEnum.UPDATE_DOWNLOAD;
        }
        if (resourceAuthorities.stream()
                .filter(resourceAuthority -> resourceAuthority.getShowed() != null).count() > 0) {
            rightTypeEnum = UpdateResourceRightTypeEnum.UPDATE_SHOW;
        }

        // 数据库操作
        resourceAuthorityMapperExt.insertBatch(resourceAuthorities, rightTypeEnum.getCode());

        if (typeEnum != AuthorityOperatingTypeEnum.COURSE) return;

        // 当是课程的时候，将章节等类型进行删除。
        List<Chapter> chapters = getChapter(courseId, null);
        if (CollectionUtils.isNotEmpty(chapters)) {
            ResourceAuthorityExample example = new ResourceAuthorityExample();
            example.createCriteria()
                    .andObjIdIn(chapters.stream().map(Chapter::getId).collect(Collectors.toList()))
                    .andUserIdEqualTo(userId).andOperatingTypeNotEqualTo(AuthorityOperatingTypeEnum.COURSE.getCode());
            resourceAuthorityMapper.deleteByExample(example);
        }

    }

    @Override
    public Map<String, ResourceInfoDTO> queryChapterResourceInfo(Long courseId, Long chapterId, Long stepChapterId,
                                                                 List<ResourcesTypeEnum> resourcesTypeEnums) {

        // 检查章节参数 并 获取章节信息
        ChapterBasicInfoDTO chapterInfo = checkChapterResourceParam(courseId, chapterId, resourcesTypeEnums);
        StepTypeEnum stepTypeEnum = StepTypeEnum.getByCode(chapterInfo.getStepType());
        if (!ChapterTypeEnum.isContainsExperiment(chapterInfo.getType())) {
            stepTypeEnum = StepTypeEnum.SINGLE;
        }

        // 获取课程的教师信息
        List<UserInfoDTO> courseTeachers = courseService.getCourseTeachers(courseId, null);

        // 根据章节id和类型 从resource_relation和resource表进行查询，获取到章节资源数据 并转换成Map
        List<ResourcesParam> data = convert(resourcesTypeEnums, chapterId, stepTypeEnum,
                stepChapterId == null ? null : Lists.newArrayList(stepChapterId));
        List<ResourceRelationDTO> resourceData = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(data)) {
            resourceData = resourceService.queryResourceRelation(data);
        }
        Map<ResourcesTypeEnum, List<ResourceRelationDTO>> typeMap = resourceData.stream()
                .collect(Collectors.groupingBy(o -> ResourcesTypeEnum.getByCode(o.getType())));

        List<SchoolTypeEnum> courseSchoolTypeEnums = null;
        if (resourcesTypeEnums.contains(ResourcesTypeEnum.LEARNING_RESOURCES)) {
            // 如果是学习资源，需要对实验课视频进行增加
            typeMap = convertResourceTypeMap(typeMap, chapterInfo, stepTypeEnum);

            // 课程的专业方向类型（本科、专科）
            courseSchoolTypeEnums = getSchoolTypes(courseId);
        }

        // 转换结果集
        Map<String, ResourceInfoDTO> map = convertResourceInfoDTO(courseId, chapterId, resourcesTypeEnums, typeMap,
                ChapterTypeEnum.getByCode(chapterInfo.getType()), courseTeachers, courseSchoolTypeEnums);
        if (courseSchoolTypeEnums == null) {
            return map;
        }

        ResourceInfoDTO resourceInfoDTO = new ResourceInfoDTO();
        resourceInfoDTO.setSchoolTypes(courseSchoolTypeEnums.stream()
                .map(schoolTypeEnum -> schoolTypeEnum.getMessage()).collect(Collectors.toList()));
        map.put("config", resourceInfoDTO);
        return map;
    }

    @Override
    public String getNotesContent(Long courseId, Long chapterId, NotesTypeEnum notesTypeEnum, Integer pageNumber) {

        checkAndGetChapter(courseId, chapterId);

        Notes notes = getNotes(chapterId, notesTypeEnum, pageNumber, userInfoHolder.getUserId());
        if (notes == null) return "";

        try {
            return mdHtmlFileService.readFile(LocalResourcesEnum.NOTES.getLocalUploadPath(),
                    StringUtils.join(notes.getId(), FileTypeEnum.MARKDOWN.getExtension()));
        } catch (Exception e) {

        }
        return "";
    }

    @Override
    public void updateNotesContent(Long courseId, Long chapterId, NotesParam param) {

        checkAndGetChapter(courseId, chapterId);

        NotesTypeEnum notesTypeEnum = NotesTypeEnum.getByCode(param.getNotesType());
        Integer pageNumber = param.getPageNumber();
        Long userId = userInfoHolder.getUserId();

        // 获取笔记信息
        Notes notes = getNotes(chapterId, notesTypeEnum, pageNumber, userId);

        // 数据库中无笔记，则增加一条
        if (notes == null) {
            notes = convertNotes(chapterId, notesTypeEnum.getCode(), pageNumber, userId);
            notesMapper.insertSelective(notes);
        }

        mdHtmlFileService.writeFile(param.getContent(), LocalResourcesEnum.NOTES.getLocalUploadPath(),
                StringUtils.join(notes.getId(), FileTypeEnum.MARKDOWN.getExtension()));

    }

    @Override
    public List<UserInfoDTO> getChapterTeacher(Long chapterId) {

        Chapter chapter = chapterMapper.selectByPrimaryKey(chapterId);
        if (chapter == null || chapter.getDeleted()) {
            throw new NotFoundException("暂未找到相应的章节");
        }

        Long courseId = chapter.getCourseId();
        List<UserInfoDTO> courseTeachers = courseService.getCourseTeachers(courseId, null);
        if (CollectionUtils.isEmpty(courseTeachers)) return null;

        if (userInfoHolder.isStudent()) {
            // 获取学生 这门课的班级
            List<Long> classList = classService.queryStudentClass(courseId, false);
            if (CollectionUtils.isEmpty(classList)) return null;

            courseTeachers = courseTeachers.stream().filter(userInfoDTO -> {
                Class defaultClass = classService.getDefaultClass(courseId, userInfoDTO.getId());
                return defaultClass != null && classList.contains(defaultClass.getId());
            }).collect(Collectors.toList());
        } else if (userInfoHolder.isTeacher()) {
            Class defaultClass = classService.getDefaultClass(courseId);

            courseTeachers = courseTeachers.stream().filter(userInfoDTO -> {
                Class defaultClassInfo = classService.getDefaultClass(courseId, userInfoDTO.getId());
                return Objects.equals(defaultClassInfo.getId(), defaultClass.getId());
            }).collect(Collectors.toList());
        }
        return courseTeachers;
    }

    @Override
    public List<UserInfoDTO> listStudent(Long chapterId) {
        Chapter chapter = chapterMapper.selectByPrimaryKey(chapterId);
        if (chapter == null || chapter.getDeleted()) {
            throw new NotFoundException("暂未找到相应的章节");
        }

        Long courseId = chapter.getCourseId();

        Class classInfo = null;
        if (userInfoHolder.isTeacher()) {
            classInfo = classService.getDefaultClass(courseId);
        } else if (userInfoHolder.isStudent()) {
            // 获取课程的教师
            List<Long> courseTeacherIds = courseService.getCourseTeacherIds(courseId, null);
            if (CollectionUtils.isEmpty(courseTeacherIds)) return null;

            // 获取 课程 与 教师 的默认班级
            List<Class> defaultClassList = Lists.newArrayList();
            courseTeacherIds.forEach(teacherId -> {
                defaultClassList.add(classService.getDefaultClass(courseId, teacherId));
            });

            // 获取学生 这门课的班级
            List<Long> classList = classService.queryStudentClass(courseId, false);

            List<Class> data = defaultClassList.stream()
                    .filter(aClass -> classList.contains(aClass.getId())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(data)) {
                classInfo = data.get(0);
            }
        }

        if (classInfo == null) {
            return null;
        }
        List<Long> userIds = userService.getClassUser(classInfo.getId());
        if (CollectionUtils.isEmpty(userIds)) {
            return null;
        }
        List<User> users = userService.listUser(userIds);
        return BeanUtil.batchTransform(UserInfoDTO.class, users);
    }

    @Override
    public Boolean isDefaultStudent(Long chapterId, Long userId) {
        try {
            List<UserInfoDTO> userInfoDTOS = listStudent(chapterId);
            return userInfoDTOS.stream()
                    .filter(userInfoDTO -> Objects.equals(userInfoDTO.getId(), userId))
                    .count() > 0 ? true : false;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public ChapterBasic getChapterInfo(Long chapterId) {
        List<ChapterBasicInfoDTO> data = listChapterInfo(null, Lists.newArrayList(chapterId), null);
        if (CollectionUtils.isEmpty(data)) {
            throw new NotFoundException("暂未找到相应的章节信息");
        }
        return BeanUtil.batchTransform(ChapterBasic.class, data).get(0);
    }

    @Override
    public List<Long> existTechnologyIds(List<Long> technologyIds) {

        // 获取章节基本信息集合
        ChapterBasicExample example = new ChapterBasicExample();
        example.createCriteria().andDeletedEqualTo(false);
        List<ChapterBasic> chapterBasics = chapterBasicMapper.selectByExample(example);

        // 将数据转换为List<Long>形式
        String str = chapterBasics.stream()
                .filter(courseBasic -> StringUtils.isNotBlank(courseBasic.getTechnologyIds()))
                .map(ChapterBasic::getTechnologyIds).collect(Collectors.joining(","));

        if (StringUtils.isNotBlank(str)) {
            List<Long> dbTechnologyList = Arrays.stream(str.split(","))
                    .map(Long::valueOf)
                    .distinct()
                    .collect(Collectors.toList());
            return dbTechnologyList;
        }

        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createChapter(Long courseId, ChapterParam chapterParam) {

        // 检查参数
        checkChapterParam(chapterParam);

        // 转换数据库
        ChapterBasic chapterBasic = BeanUtil.transform(ChapterBasic.class, chapterParam);

        Chapter chapter = convertChapter(courseId);

        // 插入数据库章节基本信息
        createChapter(chapterBasic, chapter);

        // 若是单个任务实验，则创建任务
        if (ChapterTypeEnum.isContainsExperiment(chapterBasic.getType())
                && !ChapterTypeEnum.isManyExperiment(chapterBasic.getType())) {
            taskService.createTask(chapter.getId(), new CreateOrUpdateTaskParam(chapterParam.getName()));
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createChapter(ChapterBasic chapterBasic, Chapter chapter) {
        chapterBasicMapper.insertSelective(chapterBasic);
        chapter.setChapterBasicId(chapterBasic.getId());
        chapterMapper.insertSelective(chapter);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateChapterInfo(Long courseId, Long chapterId, ChapterParam chapterParam, Boolean logicDeleted) {

        // 检查参数 并获取修改的章节数据库信息
        ChapterBasicInfoDTO chapterData = checkAndGetChapter(courseId, chapterId, chapterParam);

        // 获取参数和数据库是否是单任务实验
        boolean dbSingleExperiment = ChapterTypeEnum.isContainsSingleExperiment(chapterData.getType());
        boolean paramSingleExperiment = ChapterTypeEnum.isContainsSingleExperiment(chapterParam.getType());

        // 获取所有任务id/章节id
        List<Long> ids = Lists.newArrayList(chapterId);
        if (ChapterTypeEnum.isContainsExperiment(chapterData.getType())) {
            List<ChapterBasicInfoDTO> chapterBasicInfoDTOS = listTask(Lists.newArrayList(chapterId), null);
            if (CollectionUtils.isNotEmpty(chapterBasicInfoDTOS)) {
                ids.addAll(
                        chapterBasicInfoDTOS.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList()));
            }
        }

        // 参数转换
        ChapterBasic chapterBasic = BeanUtil.transform(ChapterBasic.class, chapterParam);

        // 修改章节或章节基本信息
        updateChapterBasicsOrChapter(chapterBasic, chapterData, ChapterEnum.CHAPTER);

        // 修改资源
        updateResource(courseId, chapterId, chapterParam.getType(), chapterData.getType(), logicDeleted);

        // 如果数据库不是单任务，参数是单任务，需要创建task表信息
        if (!dbSingleExperiment && paramSingleExperiment) {
            taskService.createTask(chapterId, new CreateOrUpdateTaskParam());
        }
        // 如果数据库是单任务，参数也是单任务，需要修改task信息
        if (dbSingleExperiment && paramSingleExperiment) {
            List<ChapterBasicInfoDTO> tasks = listTask(Lists.newArrayList(chapterData.getId()), null);
            updateChapterBasicsOrChapter(
                    BeanUtil.transform(ChapterBasic.class, chapterParam), tasks.get(0), ChapterEnum.TASK);
        }

        // 如果数据库之前是多步骤，参数不是多步骤，需要删除步骤及资源
        if (StepTypeEnum.getByCode(chapterData.getStepType()) == StepTypeEnum.MULTIPLE
                && StepTypeEnum.getByCode(chapterParam.getStepType()) != StepTypeEnum.MULTIPLE) {
            List<String> deleteData = deleteStep(ids);
            asyncTask.asyncDeleteStaticResources(deleteData);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateChapterBasicsOrChapter(
            ChapterBasic chapterBasic, ChapterBasicInfoDTO dbChapterData, ChapterEnum chapterEnum) {
        // 当前章节是否被其他教师占用
        List<Long> filterOccupyChapterBasics = filterOccupyChapterBasic(
                Lists.newArrayList(dbChapterData.getCourseId()), Lists.newArrayList(dbChapterData.getChapterBasicId()));

        // 如果数据为空，代表已被其他教师占用，则往数据库增加基本表并修改章节关联表
        if (CollectionUtils.isEmpty(filterOccupyChapterBasics)) {
            // 注意：技能不需要进行赋值
            chapterBasicMapper.insertSelective(chapterBasic);

            Chapter chapter = new Chapter();
            chapter.setId(dbChapterData.getId());
            chapter.setChapterBasicId(chapterBasic.getId());
            if (chapterEnum == ChapterEnum.TASK) {
                chapter.setParentId(dbChapterData.getParentId());
            }
            chapterMapper.updateByPrimaryKeySelective(chapter);
        } else {
            chapterBasic.setId(dbChapterData.getChapterBasicId());
            chapterBasic.setDeleted(false);
            chapterBasic.setCreateTime(dbChapterData.getCreateTime());
            chapterBasic.setUpdateTime(new Date());
            chapterBasicMapper.updateByPrimaryKey(chapterBasic);
        }
    }

    @Override
    public void updateChapterSelective(Chapter chapter) {
        chapterMapper.updateByPrimaryKeySelective(chapter);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateChapterFalg(Long courseId, Long chapterId, Boolean flag, Integer type) {

        if (type == null) return;

        if (Objects.equals(type, CourseFlagEnum.CHAPTER_RESOURCES_FLAG.getCode())) {
            // 获取章节及任务
            List<Long> updateChapterIds = Lists.newArrayList(chapterId);
            List<ChapterBasicInfoDTO> data = listTask(updateChapterIds, null);
            updateChapterIds.addAll(data.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList()));

            // 修改显隐
            chapterMapperExt.updateUpdateFalg(updateChapterIds, flag);
        } else {
            if (Objects.equals(type, CourseFlagEnum.COURSE_CLIPBOARD_FLAG.getCode())) {
                chapterId = null;
            }
            chapterMapperExt.updateUpdateClipboardFalg(chapterId, courseId, flag);
        }
    }

    @Override
    public Integer getClipboardFalg(Long courseId, Long chapterId) {
        Map<String, Object> map = new HashMap<>();
        map.put("chapterId", chapterId);
        Integer clipboardFalg = chapterMapperExt.getClipboardFalg(map);
        return clipboardFalg;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> deleteChapter(List<Long> courseIds, List<Long> chapterIds,
                                      Boolean logicDeleted, Boolean deleteResources, Boolean updatePreseted) {

        /**
         * 删除内容：
         * 1. 章节信息、资源、章节资源权限、笔记（学习课件笔记、备课材料笔记、实验笔记）
         * 2. 实验报告
         * 3. 学生/教师是否已学过某章节（我的课程-详情-已学过的章节打对勾）
         * 4. 随堂练习
         * 5. 教师和学生虚机、虚机配置
         * 6. 题库
         * 7. 删除随堂提问
         * 8. 删除步骤
         */

        List<ChapterBasicInfoDTO> chapters = null;
        if ((updatePreseted == null || !updatePreseted) && CollectionUtils.isNotEmpty(courseIds)) {
            // 删除章节
            chapters = deleteChapterInfo(courseIds, chapterIds, logicDeleted);
        }
        if (updatePreseted != null && updatePreseted && CollectionUtils.isNotEmpty(courseIds)) {
            // 不删除章节，只做查询
            chapters = listChapterInfo(courseIds, null, null);
            chapters = filterChapterAndTask(chapters, chapterIds);
        }
        if (CollectionUtils.isEmpty(chapters)) return null;

        // 根据结果集获取到 课程id集合 和 章节id集合 和 章节基本信息id集合 和 实验章节id集合
        List<Long> dbChapterIds = chapters.stream()
                .map(ChapterBasicInfoDTO::getId).distinct().collect(Collectors.toList());
        List<Long> experimentChapterIds = chapters.stream()
                .filter(infoDTO -> ChapterTypeEnum.isContainsExperiment(infoDTO.getType()) && infoDTO.getParentId() > 0)
                .map(ChapterBasicInfoDTO::getId).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(dbChapterIds)) return null;

        // 删除章节权限
        configService.deletePermissionByObjIds(dbChapterIds, PermissionEnum.PRACTICE_ANSWER_CONTROL);

        // 删除随堂练习
        practiceService.deletePractice(dbChapterIds);

        // 删除随堂提问
        quizManageService.deleteQuizByChapterIds(dbChapterIds);

        // 删除笔记，并获取笔记静态资源
        List<String> notesPaths = deleteNotes(dbChapterIds, null, null);

        // 删除实验报告
        experimentService.deleteByChapterIds(dbChapterIds);

        // 学生/教师 已学过
        operationService.batchDeleteOperation(dbChapterIds, null, EventCodeEnum.CHAPTER_RECORDING);
        operationService.batchDeleteOperation(dbChapterIds, null, EventCodeEnum.CHAPTER_STUDIED);

        List<String> resourcePaths = null;
        List<String> deleteStepResources = null;
        if (updatePreseted != null && updatePreseted) {
            // 把随堂练习题库的数据转换为预置课程名下，考试题库数据进行删除
            questionRepositoryService.moveQuestionToSpecifyUser(dbChapterIds, 1L);
        }
        if (updatePreseted == null || !updatePreseted) {

            // 删除章节分类
            deleteChapterCourseSort(null, dbChapterIds);

            // 删除题库（同时会删除题目的答案信息）
            questionRepositoryService.deleteQuestion(dbChapterIds);

            // 删除资源
            resourcePaths = deleteAndGetResourceRelation(dbChapterIds, null, null,
                    ResourceRelationTypeEnum.CHAPTER);

            // 删除步骤及步骤资源
            deleteStepResources = deleteStep(dbChapterIds);

            // 删除任务的实验判定
            experimentJudgeService.trueDelete(1, experimentChapterIds);
        }

        // 资源权限
        deleteResourceAuthority(AuthorityOperatingTypeEnum.CHAPTER, dbChapterIds);

        // 删除容器、镜像分配
        deleteContainerAndImage(experimentChapterIds, logicDeleted, updatePreseted);

        // 静态资源拼装
        notesPaths = CollectionUtils.isEmpty(notesPaths) ? Lists.newArrayList() : notesPaths;
        resourcePaths = CollectionUtils.isEmpty(resourcePaths) ? Lists.newArrayList() : resourcePaths;
        notesPaths.addAll(resourcePaths);
        notesPaths.addAll(CollectionUtils.isEmpty(deleteStepResources) ? Lists.newArrayList() : deleteStepResources);

        if (deleteResources) {
            asyncTask.asyncDeleteStaticResources(notesPaths);
            return null;
        }

        return notesPaths;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<ChapterBasicInfoDTO> deleteChapterInfo(
            List<Long> courseIds, List<Long> chapterIds, Boolean logicDeleted) {

        // 获取参数中所有要删除的章节及任务
        List<ChapterBasicInfoDTO> chapters = listChapterInfo(courseIds, null, null);
        chapters = filterChapterAndTask(chapters, chapterIds);
        if (CollectionUtils.isEmpty(chapters)) return null;

        // 根据得到的数据 获取课程id集合，章节基本id，章节id
        courseIds = chapters.stream()
                .map(ChapterBasicInfoDTO::getCourseId).distinct().collect(Collectors.toList());
        List<Long> chapterBasicIds = chapters.stream()
                .map(ChapterBasicInfoDTO::getChapterBasicId).distinct().collect(Collectors.toList());
        List<Long> dbChapterIds = chapters.stream()
                .map(ChapterBasicInfoDTO::getId).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(dbChapterIds)) return null;

        // 过滤被其他教师占用章节资源
        chapterBasicIds = filterOccupyChapterBasic(courseIds, chapterBasicIds);

        // 删除章节基本信息 和 章节
        deleteChapterAndChapterBasic(dbChapterIds, chapterBasicIds, logicDeleted);

        return chapters;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> deleteAndGetResourceRelation(List<Long> objIds, List<Integer> types, String name,
                                                     ResourceRelationTypeEnum resourceRelationTypeEnum) {

        if (CollectionUtils.isEmpty(objIds)) return null;

        // 根据章节id集合获取资源信息
        List<ResourceRelationDTO> resourceRelationDTOS = resourceService.getResourceRelationInfo(objIds, types,
                name == null ? null : Lists.newArrayList(name), resourceRelationTypeEnum.getCode());
        if (CollectionUtils.isEmpty(resourceRelationDTOS)) return null;

        // 根据结果集获取资源id结果集、章节id集合、章节资源结果集
        List<Long> dbObjectIds = resourceRelationDTOS.stream()
                .map(ResourceRelationDTO::getObjId).collect(Collectors.toList());
        List<Long> resourceIds = resourceRelationDTOS.stream()
                .map(ResourceRelationDTO::getResourceId).collect(Collectors.toList());
        List<Long> resourceRelationIds = resourceRelationDTOS.stream()
                .map(ResourceRelationDTO::getResourceRelationId).collect(Collectors.toList());

        // 删除章节和资源的关联关系
        resourceService.deleteResourceRelation(resourceRelationIds);

        // 过滤被其他教师占用，如果被占用，就不删除resource表
        resourceIds = filterOccupyResourceIds(resourceIds, dbObjectIds, resourceRelationTypeEnum);
        if (CollectionUtils.isEmpty(resourceIds)) return null;

        // 删除resource表数据
        resourceService.deleteBatch(resourceIds);

        List<Long> finalResourceIds = resourceIds;
        resourceRelationDTOS = resourceRelationDTOS.stream()
                .filter(resourceRelationDTO -> finalResourceIds.contains(resourceRelationDTO.getResourceId()))
                .collect(Collectors.toList());

        // 获取资源是否被其他类型所占用
        List<Resource> existResources = getExistResource(resourceRelationDTOS);
        Map<String, List<String>> fileNameMap = existResources.stream()
                .collect(Collectors.groupingBy(Resource::getFileName,
                        Collectors.mapping(Resource::getRemark, Collectors.toList())));

        // 根据之前查询出来的资源信息，将文件名进行获取到
        return resourceRelationDTOS.stream()
                .filter(resourceRelationDTO -> StringUtils.isNotBlank(resourceRelationDTO.getFileName())
                        && (CollectionUtils.isEmpty(fileNameMap.get(resourceRelationDTO.getFileName()))
                        || !fileNameMap.get(resourceRelationDTO.getFileName()).contains(resourceRelationDTO.getRemark())
                )).map(resourceRelationDTO -> LocalResourcesEnum.CHAPTER.getLocalUploadPath(
                        new String[]{resourceRelationDTO.getRemark()}, resourceRelationDTO.getFileName()))
                .collect(Collectors.toList());

    }

    @Override
    public void deleteStaticResources(List<String> names) {
        if (CollectionUtils.isEmpty(names)) return;
        asyncTask.asyncDeleteStaticResources(names);
    }

    @Override
    public List<ZtreeDTO> getCourseChapterByType(Long courseId, QuestionOriginEnum originEnum) {

        if (originEnum == null) throw new BadRequestException("类型不存在");

        // 获取预置课程
        if (originEnum == QuestionOriginEnum.PRESET) {
            List<CourseBasicDTO> courseBasicDTOS = courseService.listCourse(
                    null, true, true, CourseStatusEnum.SHARED.getCode());
            // 过滤本课程id
            courseBasicDTOS = courseBasicDTOS.stream()
                    .filter(courseBasic -> !Objects.equals(courseBasic.getId(), courseId)).collect(Collectors.toList());
            return convertZtreeDTO(courseBasicDTOS);
        }

        // 获取自定义课程
        List<ZtreeDTO> data = Lists.newArrayList();

        // 本人课程
        List<CourseDTO> ownData = courseService.queryCourseList();
        // 过滤本课程id
        ownData = ownData.stream()
                .filter(courseBasic -> !Objects.equals(courseBasic.getId(), courseId)).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(ownData)) {
            List<CourseBasicDTO> courseBasicDTOS = BeanUtil.batchTransform(CourseBasicDTO.class, ownData);
            data.add(convertZtreeDTO(courseBasicDTOS, "本人课程"));
        }

        // 其他教师课程
        List<CourseBasicDTO> otherData = courseService.listCourse(
                null, true, false, CourseStatusEnum.SHARED.getCode());
        // 过滤本课程id
        List<Long> ownCourseIds = ownData.stream().map(CourseDTO::getId).collect(Collectors.toList());
        otherData = otherData.stream()
                .filter(courseBasic -> !ownCourseIds.contains(courseBasic.getId())
                        && !Objects.equals(courseBasic.getId(), courseId))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(otherData)) {
            data.add(convertZtreeDTO(otherData, "其他教师课程"));
        }

        return data;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<Long, Long> importExistChapter(Long sourceCourseId, Long targetCourseId, List<Long> chapterTaskIds) {

        /**
         * 1. 章节信息、章节资源、笔记copy
         * 2. 随堂练习、有章节的考试题库copy
         * 3. 实验虚机的镜像配置copy
         */

        // 检查参数并获取课程信息
        checkAndGetCourse(targetCourseId);

        // 获取章节所有信息
        List<ChapterBasicInfoDTO> chapterBasics = listChapterInfo(null, chapterTaskIds, null);

        // 划分出用户选择的章节id集合 和 任务id集合，任务id集合 后面需要进行转换为章节
        List<Long> chapterIds = ChapterEnum.CHAPTER.getConvertData(chapterBasics).stream()
                .map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
        List<ChapterBasicInfoDTO> taskInfos = chapterBasics.stream()
                .filter(chapterBasicInfoDTO -> !chapterIds.contains(chapterBasicInfoDTO.getId())
                        && !chapterIds.contains(chapterBasicInfoDTO.getParentId()))
                .collect(Collectors.toList());

        // 获取该课程的教师信息
        List<UserInfoDTO> courseTeachers = courseService.getCourseTeachers(targetCourseId, null);

        // 主讲教师信息
        UserInfoDTO userInfo = courseTeachers.get(0);

        // 插入章节 返回结果集是 Map<原始的章节id,新章节id>格式
        Map<Long, Long> chapterIdMap = insertChapter(targetCourseId, chapterIds);

        // 将chapterIds的任务转换为章节,返回值为Map<原先任务id,现在任务id>
        Map<Long, Long> taskChapterMap = taskConvertChapter(targetCourseId, taskInfos);

        // 插入步骤
        Map<Long, Long> chapterStepIdMap = copyStep(chapterIdMap);

        // 插入实验判定
        if (MapUtils.isNotEmpty(chapterIdMap)) {
            chapterIdMap.forEach((oldChapterId, newChapterId) -> {
                experimentJudgeService.copy(1, oldChapterId, newChapterId);
            });
        }
        if (MapUtils.isNotEmpty(chapterStepIdMap)) {
            chapterStepIdMap.forEach((oldChapterStepId, newChapterStepId) -> {
                experimentJudgeService.copy(2, oldChapterStepId, newChapterStepId);
            });
        }
        if (MapUtils.isNotEmpty(taskChapterMap)) {
            taskChapterMap.forEach((oldTaskId, newChapterId) -> {
                experimentJudgeService.copy(1, oldTaskId, newChapterId);
            });
        }

        // 插入resource_relation表
        insertResourceRelation(chapterIdMap, null, ResourceRelationTypeEnum.CHAPTER);
        insertResourceRelation(
                taskChapterMap,
                Lists.newArrayList(ResourcesTypeEnum.EXPERIMENT_VIDEO.getCode(),
                        ResourcesTypeEnum.LABORATORY_MANUAL.getCode(),
                        ResourcesTypeEnum.LABORATORY_TARGET.getCode()),
                ResourceRelationTypeEnum.CHAPTER);

        // 迁移笔记
        if (MapUtils.isNotEmpty(chapterIdMap)) {
            copyNotes(courseTeachers.stream().map(UserInfoDTO::getId).collect(Collectors.toList()),
                    targetCourseId, chapterIdMap);
        }


        Map<Long, List<ChapterBasicInfoDTO>> chapterCourseMap = Maps.newHashMap();
        if (sourceCourseId == null && MapUtils.isNotEmpty(chapterIdMap)) {
            List<ChapterBasicInfoDTO> chapters = listChapterInfo(
                    null, Lists.newArrayList(chapterIdMap.keySet()), null);
            chapterCourseMap = BeanUtil.aggByKeyToList("id", chapters);
        }

        Map<Long, List<ChapterBasicInfoDTO>> finalChapterCourseMap = chapterCourseMap;
        chapterIdMap.forEach((sourceChapterId, targetChapterId) -> {
            List<ChapterBasicInfoDTO> data = finalChapterCourseMap.get(sourceChapterId);
            if (CollectionUtils.isNotEmpty(data) || sourceCourseId != null) {
                // 拷贝章节下的随堂练习 及 有章节下的考试题库
                questionRepositoryService.copyRepository(
                        sourceCourseId == null ? data.get(0).getCourseId() : sourceCourseId,
                        sourceChapterId, targetChapterId, targetCourseId, userInfo.getId(),
                        IdentityEnum.TEACHER == IdentityEnum.getByRole(userInfo.getRole()));
            }

            // 实验虚机的镜像配置copy  注意：这块没有判断章节类型是否是实验课，就直接调用接口。因为内部已经有查sql判断
            imageAllocationService.copyImageAllocation(
                    ContextTypeEnum.TASK.getCode(), sourceChapterId, targetChapterId);
        });
        if (MapUtils.isNotEmpty(taskChapterMap)) {
            taskChapterMap.forEach((oldTaskId, newChapterId) -> {
                imageAllocationService.copyImageAllocation(
                        ContextTypeEnum.TASK.getCode(), oldTaskId, newChapterId);
            });
        }

        return chapterIdMap;

    }

    @Override
    public void orderAdjustmentsChapter(Long courseId, List<Long> chapterIds) {

        // 检查是否应该修改
        checkOrderAdjustmentsChapter(courseId, chapterIds);

        // 转换数据
        List<Chapter> chapters = chapterIds.stream().map(chapterId -> {
            Chapter chapter = new Chapter();
            chapter.setId(chapterId);
            return chapter;
        }).collect(Collectors.toList());

        // 批量修改章节顺序
        updateBatch(chapters);

    }

    @Override
    public List<ZtreeDTO> getChapterOwner(Long courseId) {
        List<CourseBasicDTO> courseDTOS = courseService.listCourse(
                Lists.newArrayList(courseId), null, null, null);
        return convertZtreeDTO(courseDTOS);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createOrUpdateResource(Long courseId, Long chapterId, Long stepId, ResourcesTypeEnum typeEnum,
                                       MultipartFile[] files, CreateResourceParam param) {

        // 检查参数
        ChapterBasicInfoDTO chapterInfo = checkCreateOrUppdateResourceParam(
                courseId, chapterId, typeEnum, files, param);
        StepTypeEnum stepTypeEnum = StepTypeEnum.getByCode(chapterInfo.getStepType());
        if (!ChapterTypeEnum.isContainsExperiment(chapterInfo.getType())) {
            stepTypeEnum = StepTypeEnum.SINGLE;
        }

        // 转换格式
        ResourceRelationTypeEnum resourceRelationTypeEnum = ResourceRelationTypeEnum.CHAPTER;
        Long objId = chapterId;
        if ((typeEnum == ResourcesTypeEnum.LABORATORY_MANUAL || typeEnum == ResourcesTypeEnum.EXPERIMENT_VIDEO)
                && stepTypeEnum == StepTypeEnum.MULTIPLE) {
            objId = stepId;
            resourceRelationTypeEnum = ResourceRelationTypeEnum.STEP;
            if (objId == null) {
                throw new BadRequestException();
            }
        }

        // 是否同步，并且是什么类型
        Map<Boolean, ResourcesTypeEnum> synchronizeResource = getSynchronizeResourceMap(typeEnum, param.getName());

        // 获取查询的类型
        List<Integer> typesList = getQueryType(synchronizeResource, typeEnum);

        boolean isConvert = false;
        UploadTypeEnum uploadTypeEnum = files == null ? UploadTypeEnum.MARKDOWN : UploadTypeEnum.FILE;
        String filePath = LocalResourcesEnum.CHAPTER.getLocalUploadPath(
                new String[]{String.valueOf(courseId)}, "");
        String originalFileName = null;
        if (uploadTypeEnum == UploadTypeEnum.FILE && files.length > 0) {
            for (MultipartFile file : files) {
                originalFileName = getFileName(file.getOriginalFilename());

                if (typesList.contains(ResourcesTypeEnum.COURSEWARE.getCode())
                        && FilenameUtils.getExtension(originalFileName).equalsIgnoreCase("pptx")) {
                    isConvert = files.length == 1 ? true : false;
                    break;
                }
                if (typesList.contains(ResourcesTypeEnum.COURSEWARE.getCode())
                        && files.length == 1
                        && FilenameUtils.getExtension(originalFileName).equalsIgnoreCase("pdf")) {
                    break;
                }

            }
        }

        // 获取类型及类型对应的文件名称
        Map<Integer, List<String>> typeFileNamesMap = getTypeFileNameMap(typeEnum, typesList, uploadTypeEnum,
                originalFileName, chapterId,
                resourceRelationTypeEnum == ResourceRelationTypeEnum.STEP ? objId : null, filePath);
        // 删除资源
        List<String> data = deleteResource(objId, Lists.newArrayList(typeFileNamesMap.keySet()),
                param.getName(), resourceRelationTypeEnum);

        // 插入操作
        insertResourceAndRelation(courseId, objId, resourceRelationTypeEnum, typeFileNamesMap, param.getName());

        // 所有文件名称
        List<String> fileNameList = Arrays.stream(typeFileNamesMap.values().stream()
                .map(strs -> StringUtils.join(strs, ","))
                .collect(Collectors.joining(",")).split(","))
                .distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(fileNameList)) return;
        if (CollectionUtils.isNotEmpty(fileNameList) && CollectionUtils.isNotEmpty(data)) {
            data = data.stream().filter(s -> !fileNameList.contains(s)).collect(Collectors.toList());
        }

        if (CollectionUtils.isNotEmpty(data)) {
            data.forEach(path -> FileUtil.del(path));
        }

        // 下载md文件和html文件
        if (uploadTypeEnum == UploadTypeEnum.MARKDOWN && CollectionUtils.isNotEmpty(fileNameList)) {
            fileNameList.forEach(s -> {
                if (s.contains(FileTypeEnum.MARKDOWN.getExtension())) {
                    mdHtmlFileService.writeFile(param.getContent(), filePath, s);
                }
                if (s.contains(FileTypeEnum.HTML.getExtension())) {
                    mdHtmlFileService.writeFile(param.getHtmlContent(), filePath, s);
                }
            });
            return;
        }

        // 下载文件
        FileUtil.mkdir(filePath);
        List<String> fileNames = typeFileNamesMap.get(typeEnum.getCode());
        Map<String, String> extensionFileNameMap = Maps.newHashMap();
        fileNames.forEach(fileName -> extensionFileNameMap.put(FilenameUtils.getExtension(fileName), fileName));
        for (MultipartFile file : files) {
            String extension = FilenameUtils.getExtension(file.getOriginalFilename()).toLowerCase();
            if (StringUtils.isBlank(extension) || StringUtils.isBlank(extensionFileNameMap.get(extension))) {
                throw new GlobalException("文件格式错误");
            }
            try {
                file.transferTo(new File(
                        StringUtils.join(filePath, File.separator, extensionFileNameMap.get(extension))));
            } catch (IOException e) {
                throw new GlobalException("上传失败");
            }
        }

        // 转换文件
        if (isConvert) {
            Boolean convertFlag = convertFile(filePath, fileNameList);
            if (!convertFlag) {
                throw new GlobalException("转换出错，上传失败");
            }
        }
    }

    @Override
    public Long createOrUpdateResource(Long courseId, Long resourceId) {

        // 先将原来的文件资源包进行删除
        String fileName = ResourcesTypeEnum.PRESET_RESOURCE.getMessage() + ".zip";
        String zipFilePath = LocalResourcesEnum.COURSE.getLocalUploadPath(
                new String[]{String.valueOf(courseId)}, fileName);
        String courseTmpFilePath = LocalResourcesEnum.COURSE_TMP.getLocalUploadPath(
                new String[]{String.valueOf(courseId)}, "");
        FileUtil.del(zipFilePath);
        FileUtil.del(courseTmpFilePath);

        // 获取课程下的所有章节（包括任务）
        List<ChapterBasicInfoDTO> chapters = listChapter(
                Lists.newArrayList(courseId), null, null, null);
        if (CollectionUtils.isEmpty(chapters)) throw new NotFoundException("已有的预置资源包已删除，但暂无章节，无法再次打包");

        // 获取章节和任务的资源
        List<Long> chapterIds = chapters.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
        List<ResourceRelationDTO> chapterResources = resourceService.getResourceRelationInfo(chapterIds,
                Lists.newArrayList(ResourcesTypeEnum.LEARNING_RESOURCES.getCode(),
                        ResourcesTypeEnum.OTHER_RESOURCES.getCode(),
                        ResourcesTypeEnum.EXPERIMENT_VIDEO.getCode()),
                null, ResourceRelationTypeEnum.CHAPTER.getCode());

        // 获取步骤资源
        List<ResourceRelationDTO> stepResources = Lists.newArrayList();
        List<StepChapter> stepChapters = stepService.listStepChapter(chapterIds);
        if (CollectionUtils.isNotEmpty(stepChapters)) {
            List<Long> stepChapterIds = stepChapters.stream().map(StepChapter::getId).collect(Collectors.toList());
            stepResources = resourceService.getResourceRelationInfo(stepChapterIds,
                    Lists.newArrayList(ResourcesTypeEnum.EXPERIMENT_VIDEO.getCode()),
                    null, ResourceRelationTypeEnum.STEP.getCode());
        }

        if (CollectionUtils.isEmpty(chapterResources) && CollectionUtils.isEmpty(stepResources)) {
            throw new NotFoundException("已有的预置资源包已删除，但章节暂无资源，无法再次打包");
        }

        List<ChapterBasicInfoDTO> tasks = chapters.stream()
                .filter(chapter -> chapter.getParentId() != null && chapter.getParentId() > 0)
                .collect(Collectors.toList());
        Map<Long, List<ChapterBasicInfoDTO>> taskMap = BeanUtil.aggByKeyToList("parentId", tasks);
        List<ChapterBasicInfoDTO> chapterInfos = ChapterEnum.CHAPTER.getConvertData(chapters);
        Map<Long, List<ResourceRelationDTO>> chapterResourcesMap = BeanUtil.aggByKeyToList(
                "objId", chapterResources);
        Map<Long, List<ResourceRelationDTO>> stepResourcesMap = BeanUtil.aggByKeyToList(
                "objId", stepResources);
        Map<Long, List<StepChapter>> stepChapterMap = BeanUtil.aggByKeyToList(
                "chapterId", stepChapters);

        for (ChapterBasicInfoDTO chapter : chapterInfos) {
            // 获取章节资源
            List<ResourceRelationDTO> chapterResourceList = chapterResourcesMap.get(chapter.getId());

            // 获取任务资源
            List<ResourceRelationDTO> taskResourceList = Lists.newArrayList();
            List<ResourceRelationDTO> stepResourceList = Lists.newArrayList();
            List<ChapterBasicInfoDTO> taskInfos = taskMap.get(chapter.getId());
            if (CollectionUtils.isNotEmpty(taskInfos)) {
                taskInfos.forEach(task -> {
                    List<ResourceRelationDTO> taskResources = chapterResourcesMap.get(task.getId());
                    if (CollectionUtils.isNotEmpty(taskResources)) {
                        taskResourceList.addAll(taskResources);
                    }

                    // 获取步骤资源
                    List<StepChapter> stepChapterList = stepChapterMap.get(task.getId());

                    if (CollectionUtils.isNotEmpty(stepChapterList)) {
                        stepChapterList.forEach(stepChapter -> {
                            List<ResourceRelationDTO> data = stepResourcesMap.get(stepChapter.getId());
                            if (CollectionUtils.isNotEmpty(data)) {
                                stepResourceList.addAll(data);
                            }
                        });

                    }
                });

            }

            if (CollectionUtils.isEmpty(chapterResourceList) && CollectionUtils.isEmpty(taskResourceList)
                    && CollectionUtils.isEmpty(stepResourceList)) {
                continue;
            }

            // 章节下有资源，需要创作文件夹
            String filePath = LocalResourcesEnum.COURSE_TMP.getLocalUploadPath(
                    new String[]{String.valueOf(courseId)}, chapter.getOrderNumber() + "." + chapter.getName());
            FileUtil.mkdir(filePath);

            // copy资源
            List<ResourceRelationDTO> resources = Lists.newArrayList();
            resources.addAll(CollectionUtils.isEmpty(chapterResourceList) ? Lists.newArrayList() : chapterResourceList);
            resources.addAll(taskResourceList);
            resources.addAll(stepResourceList);
            resources.forEach(resourceRelationDTO -> {

                String originFile = LocalResourcesEnum.CHAPTER.getLocalUploadPath(
                        new String[]{resourceRelationDTO.getRemark()}, resourceRelationDTO.getFileName());
                String newFile = filePath + File.separator + resourceRelationDTO.getFileName();

                if (!new File(originFile).exists()) {
                    throw new NotFoundException(originFile + "不存在，打包失败");
                }

                try (BufferedInputStream in = FileUtil.getInputStream(originFile);
                     BufferedOutputStream out = FileUtil.getOutputStream(newFile);) {
                    IoUtil.copy(in, out);
                } catch (Exception e) {
                    LOGGER.error("originFile {} newFile {} copy error", originFile, newFile, e);
                    throw new GlobalException("文件copy错误");
                }

            });

        }

        ZipUtil.zip(courseTmpFilePath, zipFilePath);
        FileUtil.del(courseTmpFilePath);

        if (resourceId == null) {
            // 添加资源表
            resourceId = insertResource(ResourcesTypeEnum.PRESET_RESOURCE.getCode(),
                    ResourcesTypeEnum.PRESET_RESOURCE.getMessage(), fileName, String.valueOf(courseId));
        }

        return resourceId;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteResource(Long courseId, Long chapterId, Long stepChapterId,
                               ResourcesTypeEnum typeEnum, String name) {

        // 检查参数
        checkParam(courseId, chapterId, typeEnum, name);

        // 检查步骤章节id
        checkStepChapterId(stepChapterId, chapterId);

        // 是否同步，并且是什么类型
        Map<Boolean, ResourcesTypeEnum> synchronizeResource = getSynchronizeResourceMap(typeEnum, name);

        // 获取查询的类型
        List<Integer> types = getQueryType(synchronizeResource, typeEnum);

        // 删除笔记
        List<String> notes = null;
        if (stepChapterId == null) {
            notes = deleteNotes(typeEnum, name, chapterId);
        }

        // 删除资源及其他信息
        List<String> fileNames = deleteAndGetResourceRelation(
                stepChapterId == null ? Lists.newArrayList(chapterId) : Lists.newArrayList(stepChapterId),
                types, name,
                stepChapterId == null ? ResourceRelationTypeEnum.CHAPTER : ResourceRelationTypeEnum.STEP);

        // 删除静态资源
        List<String> resultPath = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(notes)) {
            resultPath.addAll(notes);
        }
        if (CollectionUtils.isNotEmpty(fileNames)) {
            resultPath.addAll(fileNames);
        }
        deleteStaticResources(resultPath);

    }


    @Override
    public Map<Long, List<ResourceAuthorityDTO>> queryResourceAuthority(
            Long userId, Map<ResourcesTypeEnum, List<String>> typeEnumListMap,
            AuthorityOperatingTypeEnum typeEnum, List<Long> objIds) {

        if (MapUtils.isEmpty(typeEnumListMap) || CollectionUtils.isEmpty(objIds)) {
            throw new BadRequestException();
        }

        Map<Long, List<ResourceAuthorityDTO>> resultMap = Maps.newHashMap();

        objIds = objIds.stream().distinct().collect(Collectors.toList());

        objIds.forEach(objId -> {
            ResourceAuthorityExample example = new ResourceAuthorityExample();
            typeEnumListMap.forEach((resourcesTypeEnum, names) -> {
                if (CollectionUtils.isEmpty(names)) {
                    names = Lists.newArrayList("");
                }
                names.forEach(name -> {
                    ResourceAuthorityExample.Criteria criteria = example.createCriteria();
                    criteria.andUserIdEqualTo(userId);
                    criteria.andOperatingTypeEqualTo(typeEnum.getCode());
                    criteria.andResourceTypeEqualTo(resourcesTypeEnum.getCode());
                    criteria.andObjIdEqualTo(objId);
                    criteria.andRemarkEqualTo(name);
                    example.or(criteria);
                });
            });

            List<ResourceAuthority> resourceAuthorities = resourceAuthorityMapper.selectByExample(example);
            if (CollectionUtils.isEmpty(resourceAuthorities)) return;

            List<ResourceAuthorityDTO> data = BeanUtil.batchTransform(ResourceAuthorityDTO.class, resourceAuthorities);
            data.forEach(resourceAuthorityDTO -> {
                resourceAuthorityDTO.setRemark(StringUtils.isBlank(resourceAuthorityDTO.getRemark()) ?
                        ResourcesTypeEnum.getByCode(resourceAuthorityDTO.getResourceType()).getMessage() :
                        resourceAuthorityDTO.getRemark());
            });

            resultMap.put(objId, data);
        });

        return resultMap;
    }

    @Override
    public List<String> deleteNotes(List<Long> chapterIds, List<Long> userIds, List<NotesTypeEnum> notesTypeEnums) {
        NotesExample example = new NotesExample();
        NotesExample.Criteria criteria = example.createCriteria();
        if (CollectionUtils.isNotEmpty(chapterIds)) {
            criteria.andChapterIdIn(chapterIds);
        }
        if (CollectionUtils.isNotEmpty(userIds)) {
            criteria.andUserIdIn(userIds);
        }
        if (CollectionUtils.isNotEmpty(notesTypeEnums)) {
            criteria.andTypeIn(notesTypeEnums.stream().map(NotesTypeEnum::getCode).collect(Collectors.toList()));
        }
        List<Notes> notes = notesMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(notes)) return null;

        notesMapper.deleteByExample(example);

        // 笔记路径名称
        return notes.stream()
                .map(note -> LocalResourcesEnum.NOTES.getLocalUploadPath(
                        StringUtils.join(note.getId(), FileTypeEnum.MARKDOWN.getExtension())))
                .collect(Collectors.toList());
    }

    @Override
    public List<Resource> getResource(Integer type, String fileName, String remark) {
        ResourceExample resourceExample = new ResourceExample();
        ResourceExample.Criteria criteria = resourceExample.createCriteria();
        if (type != null) {
            criteria.andTypeEqualTo(type);
        }
        if (StringUtils.isNotBlank(fileName)) {
            criteria.andFileNameEqualTo(fileName);
        }
        if (StringUtils.isNotBlank(remark)) {
            criteria.andRemarkEqualTo(remark);
        }
        return resourceService.queryResource(resourceExample);
    }

    @Override
    public void deleteResourceAuthority(AuthorityOperatingTypeEnum typeEnum, List<Long> objIds) {
        if (CollectionUtils.isEmpty(objIds) || typeEnum == null) return;
        ResourceAuthorityExample example = new ResourceAuthorityExample();
        example.createCriteria().andOperatingTypeEqualTo(typeEnum.getCode()).andObjIdIn(objIds);
        resourceAuthorityMapper.deleteByExample(example);
    }

    @Override
    public void checkResource(Long courseId, FileParam param) {

        // 学生是否考试
        Map<Long, Boolean> studentIsTestingMap = courseService.getStudentIsTesting(Lists.newArrayList(courseId));
        if (MapUtils.isNotEmpty(studentIsTestingMap) && studentIsTestingMap.get(courseId)) {
            throw new DownloadFileException("正在考试，无法下载资源");
        }

        String file = LocalResourcesEnum.CHAPTER.getLocalUploadPath(
                new String[]{param.getRemark()}, param.getFileName());
        if (!FileUtil.exist(file)) {
            throw new DownloadFileException("暂无资源");
        }
    }

    @Override
    public FileParam getTokenValueAndRemove(String token) {

        // 设置token
        token = StringUtils.join(userInfoHolder.getUserId(), token);

        // 获取参数的值
        FileParam param = UserConstant.DOWNLOAD_FILE_MAP.get(token);
        if (param == null) {
            throw new BadRequestException();
        }

        // token存在，删除token
        UserConstant.DOWNLOAD_FILE_MAP.remove(token);

        // 检查资源
        if (param.isExpired()) {
            throw new GlobalException("token已过期");
        }
        return param;
    }

    @Override
    public Long getCourseIdByChapterId(Long chapterId) {
        Chapter chapter = chapterMapper.selectByPrimaryKey(chapterId);
        if (chapter == null) {
            return null;
        }
        return chapter.getCourseId();
    }

    @Override
    public List<ChapterCourseSort> queryChapterCourseSort(List<Long> courseSortIds, List<Long> chapterIds) {
        if (CollectionUtils.isEmpty(courseSortIds) && CollectionUtils.isEmpty(chapterIds)) return null;
        ChapterCourseSortExample example = new ChapterCourseSortExample();
        example.setOrderByClause("id ASC");
        ChapterCourseSortExample.Criteria criteria = example.createCriteria();
        if (CollectionUtils.isNotEmpty(courseSortIds)) {
            criteria.andCourseSortIdIn(courseSortIds);
        }
        if (CollectionUtils.isNotEmpty(chapterIds)) {
            criteria.andChapterIdIn(chapterIds);
        }
        return chapterCourseSortMapper.selectByExample(example);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteChapterCourseSort(List<Long> courseSortIds, List<Long> chapterIds) {
        if (CollectionUtils.isEmpty(courseSortIds) && CollectionUtils.isEmpty(chapterIds)) return;
        ChapterCourseSortExample example = new ChapterCourseSortExample();
        ChapterCourseSortExample.Criteria criteria = example.createCriteria();
        if (CollectionUtils.isNotEmpty(courseSortIds)) {
            criteria.andCourseSortIdIn(courseSortIds);
        }
        if (CollectionUtils.isNotEmpty(chapterIds)) {
            criteria.andChapterIdIn(chapterIds);
        }
        chapterCourseSortMapper.deleteByExample(example);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createOrUpdateChapterCourseSort(Long courseId, List<CreateChapterCourseSortParam> params) {

        // 检查章节
        params = checkAndConvertChapterCourseSort(courseId, params);

        // 删除课程分类与章节的关联关系
        deleteChapterCourseSort(
                params.stream().map(CreateChapterCourseSortParam::getId).collect(Collectors.toList()), null);

        // 转换
        List<ChapterCourseSort> chapterCourseSorts = convertChapterCourseSort(params);

        // 批量插入数据库
        if (CollectionUtils.isNotEmpty(chapterCourseSorts)) {
            insertBatchChapterCourseSort(chapterCourseSorts);
        }

    }

    @Override
    public List<CourseSortDTO> listChapterCourseSort(Long courseId) {

        // 获取课程分类数据
        List<CourseSort> courseSorts = courseService.queryCourseSort(Lists.newArrayList(courseId));
        List<Long> courseSortIds = courseSorts.stream().map(CourseSort::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(courseSortIds)) throw new NotFoundException("");

        // 获取章节与课程分类的关联关系
        List<ChapterCourseSort> chapterCourseSorts = queryChapterCourseSort(courseSortIds, null);

        return convertCourseSort(courseSorts, chapterCourseSorts);
    }

    @Override
    public void updateBatch(List<Chapter> chapters) {
        chapterMapperExt.updateBatch(chapters);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insertResource(Integer type, String name, String fileName, String remark) {
        Resource resource = new Resource();
        resource.setType(type);
        resource.setName(name);
        resource.setFileName(fileName);
        resource.setRemark(remark);
        resourceService.insertResource(resource);
        return resource.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertBatchChapterBasic(List<ChapterBasic> chapterBasics) {
        chapterMapperExt.insertBatchChapterBasic(chapterBasics);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertBatchChapter(List<Chapter> chapters) {
        chapterMapperExt.insertBatch(chapters);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertBatchChapterCourseSort(List<ChapterCourseSort> chapterCourseSorts) {
        chapterCourseSortMapperExt.insertBatch(chapterCourseSorts);
    }

    @Override
    public List<ChapterBasicInfoDTO> listChapterByStepType(Long courseId, ChapterEnum chapterEnum,
                                                           StepTypeEnum typeEnum, List<ChapterTypeEnum> types) {

        Integer parent = null;
        List<Integer> intTypes = null;
        if (chapterEnum == ChapterEnum.CHAPTER) {
            parent = 0;
        }
        if (chapterEnum == ChapterEnum.TASK) {
            parent = 1;
        }
        if (types != null && types.size() > 0) {

            intTypes = Lists.newArrayList();
            for (ChapterTypeEnum type : types) {
                intTypes.add(type.getCode());
            }
        }

        return chapterMapperExt.listChapterByStepType(courseId, typeEnum == null ? null : typeEnum.getCode(), parent, intTypes);
    }

    private List<CourseSortDTO> convertCourseSort(
            List<CourseSort> courseSorts, List<ChapterCourseSort> chapterCourseSorts) {

        List<CourseSortDTO> courseSortDTOS = BeanUtil.batchTransform(CourseSortDTO.class, courseSorts);

        // 获取章节的信息
        List<Long> chapterIds = chapterCourseSorts.stream()
                .map(ChapterCourseSort::getChapterId).collect(Collectors.toList());
        List<ChapterBasicInfoDTO> chapters = listChapter(null, chapterIds, null, ChapterEnum.CHAPTER);
        Map<Long, ChapterBasicInfoDTO> chapterMap = BeanUtil.mapByKey("id", chapters);

        courseSortDTOS.forEach(courseSort -> {
            List<Long> chapterIdList = chapterCourseSorts.stream()
                    .filter(chapterCourseSort ->
                            Objects.equals(chapterCourseSort.getCourseSortId(), courseSort.getId()))
                    .map(ChapterCourseSort::getChapterId)
                    .collect(Collectors.toList());
            List<ChapterBasicInfoDTO> chapterList = Lists.newArrayList();
            chapterIdList.forEach(chapterId -> {
                ChapterBasicInfoDTO chapterBasicInfoDTO = chapterMap.get(chapterId);
                if (chapterBasicInfoDTO != null) {
                    chapterList.add(chapterBasicInfoDTO);
                }
            });

            courseSort.setChapterList(chapterList);
        });

        return courseSortDTOS;

    }

    private List<ChapterCourseSort> convertChapterCourseSort(List<CreateChapterCourseSortParam> params) {
        List<ChapterCourseSort> data = Lists.newArrayList();
        params.forEach(createChapterCourseSortParam -> {
            List<Long> chapterIds = createChapterCourseSortParam.getChapterIds();
            if (CollectionUtils.isEmpty(chapterIds)) return;

            chapterIds.forEach(chapterId -> {
                ChapterCourseSort chapterCourseSort = new ChapterCourseSort();
                chapterCourseSort.setCourseSortId(createChapterCourseSortParam.getId());
                chapterCourseSort.setChapterId(chapterId);
                data.add(chapterCourseSort);
            });
        });
        return data;
    }

    private List<CreateChapterCourseSortParam> checkAndConvertChapterCourseSort(
            Long courseId, List<CreateChapterCourseSortParam> params) {

        /**
         * 1. 检查参数与数据库的课程分类是否保持一致
         * 2. 检查参数的章节是否正确
         */

        // 获取数据库的课程分类数据
        List<CourseSort> dbCourseSortData = courseService.queryCourseSort(Lists.newArrayList(courseId));
        List<Long> dbCourseSortIds = dbCourseSortData.stream().map(CourseSort::getId).collect(Collectors.toList());

        // 获取数据库的数据与参数的数据的课程分类是否一致。
        List<Long> paramCourseSortIds = params.stream()
                .map(CreateChapterCourseSortParam::getId).distinct().collect(Collectors.toList());

        if (!CollectionUtils.isEqualCollection(dbCourseSortIds, paramCourseSortIds)) {
            throw new BadRequestException();
        }

        // 转换数据
        params.forEach(param -> {
            if (CollectionUtils.isEmpty(param.getChapterIds()) && CollectionUtils.isNotEmpty(param.getChapterList())) {
                param.setChapterIds(
                        param.getChapterList().stream().map(Chapter::getId).collect(Collectors.toList()));
            }
        });

        String data = params.stream()
                .filter(param -> CollectionUtils.isNotEmpty(param.getChapterIds()))
                .map(param -> StringUtils.join(param.getChapterIds(), ","))
                .collect(Collectors.joining(","));

        if (StringUtils.isEmpty(data)) return params;

        // 获取章节是否正确
        List<Long> chapterIds = Arrays.stream(data.split(",")).map(Long::valueOf).collect(Collectors.toList());
        List<ChapterBasicInfoDTO> chapterBasicInfoDTOS = listChapter(
                Lists.newArrayList(courseId), chapterIds, null, ChapterEnum.CHAPTER);
        if (!Objects.equals(chapterBasicInfoDTOS.size(), chapterIds.size())) {
            throw new BadRequestException("章节不存在");
        }

        return params;
    }


    private Map<Integer, List<String>> getTypeFileNameMap(ResourcesTypeEnum typeEnum, List<Integer> typesList,
                                                          UploadTypeEnum uploadTypeEnum, String originalFileName,
                                                          Long chapterId, Long stepId, String filePath) {
//        // 是否同步，并且是什么类型
//        Map<Boolean, ResourcesTypeEnum> synchronizeResource = getSynchronizeResourceMap(typeEnum, typesList);
//
//        // 获取查询的类型
//        List<Integer> types = getQueryType(synchronizeResource, typeEnum);

        // 转换标识
        Map<Boolean, List<String>> convertMap = getConvertMap(typesList);

        // 上传格式、系统名、文件名、文件地址
        String suffix = uploadTypeEnum == UploadTypeEnum.FILE ?
                StrUtil.subAfter(originalFileName, ".", true) : null;
        String newFileNameRemoveSuffix = uploadTypeEnum == UploadTypeEnum.FILE ?
                StringUtils.join(
                        StrUtil.removeSuffix(originalFileName, "." + suffix), getNumber(chapterId, stepId)) :
                RandomUtil.simpleUUID();
        suffix = suffix != null ? suffix.toLowerCase() : null;

        // 获取类型及名称
        Map<Integer, List<String>> typeFileNameMap = getTypeFileNameMap(
                typesList, newFileNameRemoveSuffix, suffix, convertMap);

        List<String> fileNames = typeFileNameMap.get(typeEnum.getCode());
        if (typeEnum == ResourcesTypeEnum.OTHER_RESOURCES && CollectionUtils.isNotEmpty(fileNames)) {
            fileNames.forEach(s -> {
                if (FileUtil.exist(StringUtils.join(filePath, File.separator, s))) {
                    throw new GlobalException("系统中已存在此文件");
                }
            });
        }

        return typeFileNameMap;
    }

    private Map<Integer, List<String>> getTypeFileNameMap(List<Integer> types, String newFileName, String fileSuffix,
                                                          Map<Boolean, List<String>> convertMap) {
        Map<Integer, List<String>> typeFileNameMap = Maps.newHashMap();
        types.forEach(type -> {
            List<String> fileNamesAndSuffix = Lists.newArrayList();
            if (fileSuffix != null) {
                fileNamesAndSuffix.add(
                        StringUtils.isEmpty(fileSuffix) ?
                                newFileName : StringUtils.join(newFileName, ".", fileSuffix));
            }
            if (MapUtils.isNotEmpty(convertMap)
                    && convertMap.get(true) != null) {
                convertMap.get(true).forEach(s -> {
                    if (!fileNamesAndSuffix.contains(StringUtils.join(newFileName, s))) {
                        fileNamesAndSuffix.add(StringUtils.join(newFileName, s));
                    }
                });

            }
            typeFileNameMap.put(type, fileNamesAndSuffix);
        });
        return typeFileNameMap;
    }

    private Map<Boolean, List<String>> getConvertMap(List<Integer> types) {
        Map<Boolean, List<String>> convertMap = Maps.newHashMap();
        List<ResourcesTypeEnum> convertTypes = Arrays.stream(ResourcesTypeEnum.values())
                .filter(typeEnum1 -> types.contains(typeEnum1.getCode())
                        && CollectionUtils.isNotEmpty(typeEnum1.getConvertFormat()))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(convertTypes)) {
            convertMap.put(true, convertTypes.get(0).getConvertFormat());
        }
        return convertMap;
    }

    private List<Integer> getQueryType(
            Map<Boolean, ResourcesTypeEnum> synchronizeResource, ResourcesTypeEnum typeEnum) {
        List<Integer> types = Lists.newArrayList(typeEnum.getCode());
        if (synchronizeResource.get(true) != null) {
            Integer type = typeEnum == ResourcesTypeEnum.LEARNING_RESOURCES ?
                    synchronizeResource.get(true).getCode() : ResourcesTypeEnum.LEARNING_RESOURCES.getCode();
            types.add(type);
        }
        return types.stream().distinct().collect(Collectors.toList());
    }

    private Map<Boolean, ResourcesTypeEnum> getSynchronizeResourceMap(ResourcesTypeEnum typeEnum, String name) {
        Map<Boolean, ResourcesTypeEnum> synchronizeResource = Maps.newHashMap();
        List<ResourcesTypeEnum> resourcesTypeEnums = Arrays.stream(ResourcesTypeEnum.values())
                .filter(resourcesTypeEnum -> resourcesTypeEnum.getResourceSynchronized()
                        && StringUtils.isNotBlank(name)
                        && Objects.equals(name, resourcesTypeEnum.getMessage()))
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(resourcesTypeEnums)) {
            if (typeEnum.getResourceSynchronized()
                    || typeEnum == ResourcesTypeEnum.LEARNING_RESOURCES) {
                synchronizeResource.put(true, resourcesTypeEnums.get(0));
            } else {
                synchronizeResource.put(false, resourcesTypeEnums.get(0));
            }
        }
        return synchronizeResource;
    }

    private Boolean convertFile(String filePath, List<String> fileNames) {
        String url = StringUtils.join(convertProjectUrl, "/convertPPTToPDF");
        ConvertFileRequest requestObj = new ConvertFileRequest(
                StringUtils.join(filePath, File.separator, fileNames.get(0)),
                StringUtils.join(filePath, File.separator, fileNames.get(1))
        );
        try {
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestObj, String.class);
            if (responseEntity.getStatusCodeValue() == 200) {
                ConvertFileResponse convertFileResponse = JSON.parseObject(
                        responseEntity.getBody(), ConvertFileResponse.class);
                if (Objects.equals(convertFileResponse.getResult(), "success")) {
                    return true;
                }
            }
        } catch (Exception e) {
            LOGGER.error("文件名 {} 转换pdf文件失败：" + e.getMessage(), requestObj.getOriginFile(), e);
        }
        return false;
    }

    private void updateResource(List<ResourceRelationDTO> dbData, Map<Integer, List<String>> typeFileNamesMap,
                                Long courseId) {
        if (CollectionUtils.isEmpty(dbData) || MapUtils.isEmpty(typeFileNamesMap)) return;

        List<Resource> resources = Lists.newArrayList();

        Map<Integer, List<ResourceRelationDTO>> typeMap = BeanUtil.aggByKeyToList("type", dbData);

        typeMap.forEach((type, resourceRelationDTOS) -> {
            List<String> fileNames = typeFileNamesMap.get(type);
            for (int i = 0; i < resourceRelationDTOS.size(); i++) {
                ResourceRelationDTO resourceDTO = resourceRelationDTOS.get(i);
                Resource resource = new Resource();
                resource.setId(resourceDTO.getResourceId());
                resource.setFileName(fileNames.get(i));
                resource.setRemark(String.valueOf(courseId));
                resources.add(resource);
            }
        });

        if (CollectionUtils.isEmpty(resources)) {
            throw new GlobalException("");
        }
        resourceService.updateBatch(resources);
    }


    private List<ResourceRelation> convertChapterResources(List<Long> resourceIds, Long objId,
                                                           ResourceRelationTypeEnum typeEnum) {

        if (CollectionUtils.isEmpty(resourceIds) || objId == null) return null;

        List<ResourceRelation> resourceRelations = Lists.newArrayList();
        resourceIds.forEach(resourceId -> {
            ResourceRelation resourceRelation = new ResourceRelation();
            resourceRelation.setResourceId(resourceId);
            resourceRelation.setObjId(objId);
            resourceRelation.setType(typeEnum.getCode());
            resourceRelations.add(resourceRelation);
        });
        return resourceRelations;
    }

    private List<Resource> convertResource(Map<Integer, List<String>> typeFileNamesMap, String name, Long courseId) {

        if (MapUtils.isEmpty(typeFileNamesMap)) return null;

        List<Resource> resources = Lists.newArrayList();
        typeFileNamesMap.forEach((type, fileNames) -> {
            if (CollectionUtils.isNotEmpty(fileNames)) {
                fileNames.forEach(fileName -> {
                    Resource resource = new Resource();
                    resource.setName(name);
                    resource.setFileName(fileName);
                    resource.setType(type);
                    resource.setRemark(String.valueOf(courseId));
                    resources.add(resource);
                });
            }
        });
        return resources;
    }

    private ChapterBasicInfoDTO checkCreateOrUppdateResourceParam(
            Long courseId, Long chapterId, ResourcesTypeEnum typeEnum,
            MultipartFile[] files, CreateResourceParam param) {
        if (files == null && param.getContent() == null) throw new BadRequestException("");
        if (files == null && StringUtils.isBlank(param.getContent()
                .replaceAll(" ", "").replaceAll("\n", ""))) {
            param.setContent("");
            param.setHtmlContent("");
        }

        // 检查参数
        ChapterBasicInfoDTO chapterInfo = checkParam(courseId, chapterId, typeEnum, param.getName());

        // 检查其他资源中的模块名称是否重复
        if (typeEnum == ResourcesTypeEnum.OTHER_RESOURCES) {
            List<ResourceRelationDTO> data = resourceService.getResourceRelationInfo(
                    Lists.newArrayList(chapterId), Lists.newArrayList(typeEnum.getCode()),
                    Lists.newArrayList(param.getName()), ResourceRelationTypeEnum.CHAPTER.getCode());
            if (data.stream()
                    .filter(resourceRelationDTO -> Objects.equals(resourceRelationDTO.getName(), param.getName()))
                    .count() > 0) {
                throw new GlobalException(typeEnum.getMessage() + "模块名称不能重复");
            }
        }

        return chapterInfo;
    }

    private ChapterBasicInfoDTO checkParam(Long courseId, Long chapterId, ResourcesTypeEnum typeEnum, String name) {
        if (typeEnum == null) throw new BadRequestException();

        List<ChapterBasicInfoDTO> chapterData = listChapterInfo(Lists.newArrayList(courseId),
                Lists.newArrayList(chapterId), null);
        if (CollectionUtils.isEmpty(chapterData)) {
            throw new BadRequestException("章节不存在");
        }
        ChapterTypeEnum chapterTypeEnum = ChapterTypeEnum.getByCode(chapterData.get(0).getType());
        if (chapterTypeEnum == null) {
            throw new BadRequestException("章节类型不存在");
        }
        if (CollectionUtils.isEmpty(chapterTypeEnum.getResourceType())) {
            throw new BadRequestException("章节类型不存在资源");
        }
        if (!chapterTypeEnum.getResourceType().contains(typeEnum)) {
            throw new GlobalException("当前章节类型不能更改当前资源");
        }
        if (typeEnum != ResourcesTypeEnum.LEARNING_RESOURCES
                && typeEnum != ResourcesTypeEnum.OTHER_RESOURCES
                && !Objects.equals(name, typeEnum.getMessage())) {
            throw new BadRequestException();
        }
        return chapterData.get(0);
    }

    private Map<Long, Long> insertChapter(Long courseId, List<Long> chapterIds) {

        Map<Long, Long> map = Maps.newHashMap();
        if (CollectionUtils.isEmpty(chapterIds)) return map;

        // 根据章节id集合获取基本信息
        List<ChapterBasicInfoDTO> chapterBasics = listChapterInfo(null, chapterIds, null);

        List<Long> dbChapterIds = ChapterEnum.CHAPTER.getConvertData(chapterBasics).stream()
                .map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
        if (!CollectionUtils.isEqualCollection(chapterIds, dbChapterIds)) {
            throw new GlobalException("章节有变动，请刷新重新选择");
        }

        // 获取现在课程的章节序号
        Integer orderNumber = getOrderNumber(courseId);

        // 转换章节
        List<Chapter> chapters = convertChapterBasicList(
                ChapterEnum.CHAPTER, chapterBasics, orderNumber, courseId, null);
        List<Long> chapterIdList = ChapterEnum.CHAPTER.getConvertData(chapterBasics).stream()
                .map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(chapters)) {
            throw new BadRequestException("章节信息不存在");
        }

        // 将章节插入数据库 并获取原始id与新id放到集合中
        insertBatchChapter(chapters);
        for (int i = 0; i < chapters.size(); i++) {
            map.put(chapterIdList.get(i), chapters.get(i).getId());
        }

        // 获取任务并插入数据库 并将任务放到map中
        chapterBasics = listTask(
                chapterBasics.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList()), null);
        List<Chapter> tasks = convertChapterBasicList(
                ChapterEnum.TASK, chapterBasics, null, courseId, map);
        if (CollectionUtils.isNotEmpty(tasks)) {
            List<Long> taskIds = ChapterEnum.TASK.getConvertData(chapterBasics).stream()
                    .map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
            insertBatchChapter(tasks);
            for (int i = 0; i < tasks.size(); i++) {
                map.put(taskIds.get(i), tasks.get(i).getId());
            }
        }

        return map;
    }

    private void insertResourceRelation(Map<Long, Long> objIdMap, List<Integer> types,
                                        ResourceRelationTypeEnum resourceRelationTypeEnum) {

        if (MapUtils.isEmpty(objIdMap)) return;

        // 查询数据库资源信息情况
        List<ResourceRelationDTO> resourceRelationDTOS = resourceService.getResourceRelationInfo(
                Lists.newArrayList(objIdMap.keySet()), types, null,
                resourceRelationTypeEnum.getCode());
        if (CollectionUtils.isEmpty(resourceRelationDTOS)) return;

        Map<Long, List<ResourceRelationDTO>> dbObjMap = BeanUtil.aggByKeyToList("objId", resourceRelationDTOS);

        // 转换结果集
        List<ResourceRelation> resourceRelations = Lists.newArrayList();
        objIdMap.forEach((dbObjId, insertObjId) -> {

            List<ResourceRelationDTO> data = dbObjMap.get(dbObjId);
            if (CollectionUtils.isEmpty(data)) return;

            List<Long> dbResourceIds = data.stream()
                    .map(ResourceRelationDTO::getResourceId).collect(Collectors.toList());
            dbResourceIds.forEach(resourceId -> {
                ResourceRelation resourceRelation = new ResourceRelation();
                resourceRelation.setObjId(insertObjId);
                resourceRelation.setResourceId(resourceId);
                resourceRelation.setType(resourceRelationTypeEnum.getCode());
                resourceRelations.add(resourceRelation);
            });
        });

        // 插入数据库
        resourceService.insertBatchResourceRelation(resourceRelations);

    }

    private List<ZtreeDTO> convertZtreeDTO(List<CourseBasicDTO> courseDTOS) {

        if (CollectionUtils.isEmpty(courseDTOS)) {
            return Lists.newArrayList();
        }

        // 将课程信息转换成ztree格式
        List<ZtreeDTO> courseZtreeDTOS = BeanUtil.batchTransform(ZtreeDTO.class, courseDTOS);
        List<Long> courseIds = courseZtreeDTOS.stream().map(ZtreeDTO::getId).collect(Collectors.toList());

        // 根据课程id获取它的章节信息
        List<ChapterBasicInfoDTO> chapterBasics = listChapter(courseIds, null, null, ChapterEnum.CHAPTER);

        //  转换为Map形式。为拼装做准备
        List<ZtreeDTO> chapterZtreeDTOS = BeanUtil.batchTransform(ZtreeDTO.class, chapterBasics);
        Map<Long, List<ZtreeDTO>> chapterMap = BeanUtil.aggByKeyToList("courseId", chapterZtreeDTOS);

        // 过滤无章节的课程 并 拼装章节信息
        courseZtreeDTOS = courseZtreeDTOS.stream()
                .filter(ztreeDTO -> chapterMap.keySet().contains(ztreeDTO.getId()))
                .map(ztreeDTO -> {
                    ztreeDTO.setType(null);
                    ztreeDTO.setChildren(chapterMap.get(ztreeDTO.getId()));
                    ztreeDTO.setId(null);
                    return ztreeDTO;
                }).collect(Collectors.toList());

        return courseZtreeDTOS;
    }

    private List<Chapter> convertChapterBasicList(
            ChapterEnum chapterEnum, List<ChapterBasicInfoDTO> chapterBasics,
            Integer orderNumber, Long courseId, Map<Long, Long> chapterIdMap) {

        if (CollectionUtils.isEmpty(chapterBasics)) return null;

        List<Chapter> result = Lists.newArrayList();

        // 获取任务数据
        List<ChapterBasicInfoDTO> chapters = chapterEnum == null ?
                chapterBasics : chapterEnum.getConvertData(chapterBasics);
        for (int i = 0; i < chapters.size(); i++) {
            ChapterBasicInfoDTO chapterBasicInfoDTO = chapters.get(i);

            // 如果是章节，则创建章节的对象
            if (chapterEnum == ChapterEnum.CHAPTER) {
                Chapter chapter = new Chapter();
                chapter.setOrderNumber(orderNumber + i);
                chapter.setChapterBasicId(chapterBasicInfoDTO.getChapterBasicId());
                chapter.setCourseId(courseId);
                chapter.setDifficultyScore(chapterBasicInfoDTO.getDifficultyScore());
                result.add(chapter);
                continue;
            }

            // 任务
            Chapter taskChapter = new Chapter();
            taskChapter.setOrderNumber(chapterBasicInfoDTO.getOrderNumber());
            taskChapter.setChapterBasicId(chapterBasicInfoDTO.getChapterBasicId());
            taskChapter.setCourseId(courseId);
            taskChapter.setParentId(chapterIdMap.get(chapterBasicInfoDTO.getParentId()));
            taskChapter.setDifficultyScore(chapterBasicInfoDTO.getDifficultyScore());
            result.add(taskChapter);

        }

        return result;
    }

    private void checkChapterParam(ChapterParam chapterParam) {

        ChapterTypeEnum chapterTypeEnum = ChapterTypeEnum.getByMessage(chapterParam.getTypeName());
        if (chapterTypeEnum == null) {
            throw new BadRequestException("章节类型不存在");
        }
        chapterParam.setType(chapterTypeEnum.getCode());

        // 技术选择的id是否数据库存在
        if (CollectionUtils.isEmpty(chapterParam.getTechnologyIdList())) {
            chapterParam.setTechnologyIds("");
        } else {
            List<Technology> technologies = technologyService.listTechnology(chapterParam.getTechnologyIdList());
            if (CollectionUtils.isEmpty(technologies)
                    || technologies.size() != chapterParam.getTechnologyIdList().size()) {
                throw new BadRequestException("技能数据异常，请刷新页面重新选择");
            }
            chapterParam.setTechnologyIds(StringUtils.join(chapterParam.getTechnologyIdList(), ","));
        }

        // 章节步骤
        if (!ChapterTypeEnum.isContainsExperiment(chapterTypeEnum.getCode())
                && StringUtils.isNotBlank(chapterParam.getStepTypeStr())) {
            throw new BadRequestException("当前章节类型不包含实验，不能进行填写实验步骤");
        }
        if (StringUtils.isNotBlank(chapterParam.getStepTypeStr())) {
            if (StepTypeEnum.getByMessage(chapterParam.getStepTypeStr()) == null) {
                throw new BadRequestException("实验步骤类型不存在");
            }
            chapterParam.setStepType(StepTypeEnum.getByMessage(chapterParam.getStepTypeStr()).getCode());
        }
        if (ChapterTypeEnum.isContainsExperiment(chapterTypeEnum.getCode())
                && StringUtils.isBlank(chapterParam.getStepTypeStr())) {
            chapterParam.setStepType(StepTypeEnum.SINGLE.getCode());
        }

    }

    private List<ChapterBasicInfoDTO> convertChapterBasicInfoDTO(List<ChapterBasicInfoDTO> data) {
        List<ChapterBasicInfoDTO> tasks = ChapterEnum.TASK.getConvertData(data);
        data = ChapterEnum.CHAPTER.getConvertData(data);
        for (ChapterBasicInfoDTO chapterData : data) {

            // 是否是多任务
            boolean manyExperimentFlag = ChapterTypeEnum.isManyExperiment(chapterData.getType());

            // 配置信息
            StepTypeEnum typeEnum = StepTypeEnum.getByCode(chapterData.getStepType());
            chapterData.setStepTypeStr(typeEnum == null ? null : typeEnum.getMessage());
            chapterData.setTypeName(ChapterTypeEnum.getByCode(chapterData.getType()).getMessage());
            chapterData.setManyExperiment(manyExperimentFlag);
            chapterData.setNumber(getNumber(chapterData.getId(), null));
            chapterData.setExperimentCourseFlag(
                    ChapterTypeEnum.isContainsExperiment(chapterData.getType()));
            if (!manyExperimentFlag && chapterData.isExperimentCourseFlag()) {
                List<ChapterBasicInfoDTO> taskInfos = tasks.stream()
                        .filter(chapterBasicInfoDTO ->
                                Objects.equals(chapterBasicInfoDTO.getParentId(), chapterData.getId()))
                        .collect(Collectors.toList());
                chapterData.setSingleTaskId(taskInfos.get(0).getId());
            }
            if (StringUtils.isNotBlank(chapterData.getTechnologyIds())) {
                chapterData.setTechnologyIdList(
                        Lists.newArrayList(chapterData.getTechnologyIds().split(",")).stream()
                                .map(Long::valueOf).collect(Collectors.toList())
                );
            }
        }
        return data;
    }

    private String getNumber(Long chapterId, Long stepId) {
        String s = "ZDY-" + String.format("%05d", chapterId);
        if (stepId != null) {
            s = s + "-" + stepId;
        }
        return s;
    }

    private Notes convertNotes(Long chapterId, Integer type, Integer pageNumber, Long userId) {
        Notes notes = new Notes();
        notes.setChapterId(chapterId);
        notes.setType(type);
        notes.setPageNumber(pageNumber);
        notes.setUserId(userId);
        return notes;
    }

    private Notes getNotes(Long chapterId, NotesTypeEnum notesTypeEnum, Integer pageNumber, Long userId) {
        NotesExample example = new NotesExample();
        example.createCriteria()
                .andChapterIdEqualTo(chapterId).andTypeEqualTo(notesTypeEnum.getCode())
                .andPageNumberEqualTo(pageNumber).andUserIdEqualTo(userId);
        List<Notes> notes = notesMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(notes)) return null;

        return notes.stream().findFirst().get();
    }

    private ChapterBasicInfoDTO checkChapterResourceParam(
            Long courseId, Long chapterId, List<ResourcesTypeEnum> resourcesTypeEnums) {
        if (CollectionUtils.isEmpty(resourcesTypeEnums)) {
            throw new BadRequestException();
        }
        return checkAndGetChapter(courseId, chapterId);
    }

    private Map<String, ResourceInfoDTO> convertResourceInfoDTO(
            Long courseId, Long chapterId, List<ResourcesTypeEnum> typeEnums,
            Map<ResourcesTypeEnum, List<ResourceRelationDTO>> typeMap, ChapterTypeEnum chapterTypeEnum,
            List<UserInfoDTO> courseTeachers, List<SchoolTypeEnum> schoolTypeEnums) {
        Map<String, ResourceInfoDTO> map = Maps.newHashMap();
        for (ResourcesTypeEnum typeEnum : typeEnums) {
            String key = Arrays.stream(StringUtils.split(typeEnum.name(), "_"))
                    .map(str -> StringUtils.join(str.substring(0, 1), str.substring(1).toLowerCase()))
                    .reduce((str1, str2) -> StringUtils.join(str1, str2))
                    .map(s -> StringUtils.join(s.substring(0, 1).toLowerCase(), s.substring(1))).get();
            map.put(key, convertResourceDTOS(courseId, chapterId, typeEnum, typeMap,
                    chapterTypeEnum, courseTeachers, schoolTypeEnums));
        }
        return map;
    }

    private List<ResourceDTO> convertResourceDTO(List<ResourceRelationDTO> data) {
        if (CollectionUtils.isEmpty(data)) return Lists.newArrayList();

        List<ResourceDTO> result = Lists.newArrayList();
        for (ResourceRelationDTO resourceRelationDTO : data) {
            ResourceDTO resourceDTO = new ResourceDTO();
            resourceDTO.setChapterId(resourceRelationDTO.getObjId());
            resourceDTO.setChapterId(resourceRelationDTO.getObjId());
            resourceDTO.setName(resourceRelationDTO.getName());
            resourceDTO.setFileName(resourceRelationDTO.getFileName());
            resourceDTO.setRemark(resourceRelationDTO.getRemark());
            resourceDTO.setResourceId(resourceRelationDTO.getResourceId());
            resourceDTO.setType(resourceRelationDTO.getType());
            resourceDTO.setChapterResourceId(resourceRelationDTO.getResourceRelationId());
            result.add(resourceDTO);
        }
        return result;
    }

    private ResourceInfoDTO convertResourceDTOS(
            Long courseId, Long chapterId, ResourcesTypeEnum resourcesTypeEnum,
            Map<ResourcesTypeEnum, List<ResourceRelationDTO>> typeMap,
            ChapterTypeEnum chapterTypeEnum, List<UserInfoDTO> courseTeachers, List<SchoolTypeEnum> schoolTypeEnums) {

        List<ResourceRelationDTO> data = typeMap.get(resourcesTypeEnum);

        // 将数据转换并按照资源id从小到大排序
        List<ResourceDTO> resourceDTOS = convertResourceDTO(data);
        resourceDTOS = resourceDTOS.stream()
                .map(resourceDTO -> {

                    // 设置文件路径
                    resourceDTO.setFilePath(LocalResourcesEnum.CHAPTER.getMappingUrl(
                            new String[]{resourceDTO.getRemark()}, resourceDTO.getFileName()));

                    // 设置markdown内容
                    if ((resourcesTypeEnum == ResourcesTypeEnum.LABORATORY_MANUAL
                            || resourcesTypeEnum == ResourcesTypeEnum.PREPARATION_MATERIALS
                            || resourcesTypeEnum == ResourcesTypeEnum.LABORATORY_TARGET)
                            && StringUtils.isNotBlank(resourceDTO.getFileName())
                            && Objects.equals(FileTypeEnum.MARKDOWN.getExtension(),
                            StringUtils.join(".",
                                    StrUtil.subAfter(resourceDTO.getFileName(), ".", true)))) {

                        resourceDTO.setFilePath("");

                        try {
                            resourceDTO.setContent(
                                    mdHtmlFileService.readFile(
                                            LocalResourcesEnum.CHAPTER.getLocalUploadPath(
                                                    new String[]{resourceDTO.getRemark()}, ""),
                                            resourceDTO.getFileName()
                                    )
                            );
                        } catch (Exception e) {
                            resourceDTO.setContent("");
                        }

                    }

                    // 设置html内容
                    if ((resourcesTypeEnum == ResourcesTypeEnum.LABORATORY_MANUAL
                            || resourcesTypeEnum == ResourcesTypeEnum.PREPARATION_MATERIALS
                            || resourcesTypeEnum == ResourcesTypeEnum.LABORATORY_TARGET)
                            && StringUtils.isNotBlank(resourceDTO.getFileName())
                            && Objects.equals(FileTypeEnum.HTML.getExtension(),
                            StringUtils.join(".",
                                    StrUtil.subAfter(resourceDTO.getFileName(), ".", true)))) {

                        resourceDTO.setFilePath("");

                        try {
                            resourceDTO.setHtmlContent(
                                    mdHtmlFileService.readFile(
                                            LocalResourcesEnum.CHAPTER.getLocalUploadPath(
                                                    new String[]{resourceDTO.getRemark()}, ""),
                                            resourceDTO.getFileName()
                                    )
                            );
                        } catch (Exception e) {
                        }
                    }

                    return resourceDTO;
                }).collect(Collectors.toList());


        // 将资源转换为创作课程结果集 和 展示版本的资源信息
        ResourceInfoDTO resourceInfoDTO = resourcesTypeEnum.getResourceInfoDTO(resourceDTOS);
        if (resourcesTypeEnum == ResourcesTypeEnum.LEARNING_RESOURCES) {
            CodeEnum codeEnum = null;
            if (CollectionUtils.isNotEmpty(schoolTypeEnums) && !schoolTypeEnums.contains(SchoolTypeEnum.BACHELOR_DEGREE)
                    && schoolTypeEnums.contains(SchoolTypeEnum.COLLEGE_DEGREE)) {
                codeEnum = CodeEnum.COLLEGE_DEGREE;
            }
            resourceInfoDTO = resourcesTypeEnum.getResourceInfoDTO(resourceDTOS, chapterTypeEnum, chapterId, codeEnum);
        }

        // 转换角色权限
        return convertResourceJurisdiction(courseId, chapterId, resourcesTypeEnum,
                resourceInfoDTO, courseTeachers, chapterTypeEnum);

    }

    private ResourceInfoDTO convertResourceJurisdiction(
            Long courseId, Long chapterId, ResourcesTypeEnum typeEnum,
            ResourceInfoDTO resourceInfoDTO, List<UserInfoDTO> courseTeachers, ChapterTypeEnum chapterTypeEnum) {

        // 设置展示版本内容为空
        resourceInfoDTO.getDisplayVersion().forEach(resourceDTO -> resourceDTO.setContent(null));

        // 学生无创作资源
        if (userInfoHolder.isStudent()) {
            resourceInfoDTO.setCreativeVersion(null);
        }

        // 通过参数 获取主讲教师的信息 以及 课程的教师id集合
        UserInfoDTO courseTeacher = courseTeachers.get(0);
        List<Long> teacherIds = courseTeachers.stream().map(UserInfoDTO::getId).collect(Collectors.toList());

        Long teacherUserId = courseTeacher.getId();
        Boolean universityFlag = false;
        if (userInfoHolder.isTeacher()) {
            teacherUserId = teacherIds.contains(userInfoHolder.getUserId()) ?
                    teacherUserId : userInfoHolder.getUserId();
            universityFlag = RoleEnum.getByCode(userInfoHolder.getUser().getRole()) == RoleEnum.UNIVERSITY_TEACHER ?
                    true : false;
        }
        if (userInfoHolder.isStudent()) {
            universityFlag = RoleEnum.getByCode(courseTeacher.getRole()) == RoleEnum.UNIVERSITY_TEACHER ?
                    true : false;
        }

        List<String> names = resourceInfoDTO.getDisplayVersion().stream()
                .map(ResourceDTO::getName).collect(Collectors.toList());

        // 查看学生的实验手册，教师是否允许显示
        if (userInfoHolder.isStudent()
                && (typeEnum == ResourcesTypeEnum.LABORATORY_MANUAL
                || typeEnum == ResourcesTypeEnum.EXPERIMENT_VIDEO)) {
            Map<ResourcesTypeEnum, List<String>> typeEnumListMap = Maps.newHashMap();
            typeEnumListMap.put(ResourcesTypeEnum.LABORATORY_MANUAL,
                    Lists.newArrayList(ResourcesTypeEnum.LABORATORY_MANUAL.getMessage()));

            Map<Long, List<ResourceAuthorityDTO>> courseAuthorityMap = queryResourceAuthority(teacherUserId,
                    typeEnumListMap, AuthorityOperatingTypeEnum.COURSE, Lists.newArrayList(courseId));
            Map<Long, List<ResourceAuthorityDTO>> chapterAuthorityMap = queryResourceAuthority(teacherUserId,
                    typeEnumListMap, AuthorityOperatingTypeEnum.CHAPTER, Lists.newArrayList(chapterId));

            List<ResourceAuthorityDTO> courseAuthorityList = courseAuthorityMap.get(courseId);
            List<ResourceAuthorityDTO> chapterAuthorityList = chapterAuthorityMap.get(chapterId);

            Map<String, Boolean> courseRemarkMap = Maps.newHashMap();
            if (CollectionUtils.isNotEmpty(courseAuthorityList)) {
                courseRemarkMap = courseAuthorityList.stream()
                        .collect(Collectors.toMap(ResourceAuthorityDTO::getRemark, ResourceAuthorityDTO::getShowed));
            }

            Map<String, Boolean> chapterRemarkMap = Maps.newHashMap();
            if (CollectionUtils.isNotEmpty(chapterAuthorityList)) {
                chapterRemarkMap = chapterAuthorityList.stream()
                        .collect(Collectors.toMap(ResourceAuthorityDTO::getRemark, ResourceAuthorityDTO::getShowed));
            }

            Boolean showed = courseRemarkMap.get(ResourcesTypeEnum.LABORATORY_MANUAL.getMessage());
            if (showed == null) {
                showed = true;
            }
            Boolean chapterShowed = chapterRemarkMap.get(
                    ResourcesTypeEnum.LABORATORY_MANUAL.getMessage());
            if (chapterShowed != null) {
                showed = chapterShowed;
                resourceInfoDTO.setShowed(showed);
            }

            Boolean finalshowed = showed;
            List<ResourceDTO> data = resourceInfoDTO.getDisplayVersion().stream()
                    .map(resourceDTO -> {
                        if (Objects.equals(ResourcesTypeEnum.LABORATORY_MANUAL.getMessage(), resourceDTO.getName())
                                || Objects.equals(ResourcesTypeEnum.EXPERIMENT_VIDEO.getMessage(),
                                resourceDTO.getName())) {
                            if (!finalshowed) {
                                resourceDTO = new ResourceDTO();
                            }
                        }
                        return resourceDTO;
                    }).collect(Collectors.toList());

            resourceInfoDTO.setDisplayVersion(data);

        }
        // 查看学生的教学资源、其他资源是否允许下载，并且设置教师的小眼睛按钮
        if (!universityFlag && (typeEnum == ResourcesTypeEnum.LEARNING_RESOURCES
                || typeEnum == ResourcesTypeEnum.OTHER_RESOURCES) && CollectionUtils.isNotEmpty(names)) {

            List<ChapterBasicInfoDTO> tasks = listTask(Lists.newArrayList(chapterId), null);
            List<Long> taskIds = tasks.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
            taskIds.add(chapterId);

            Map<ResourcesTypeEnum, List<String>> courseTypeEnumMap = Maps.newHashMap();
            Map<ResourcesTypeEnum, List<String>> chapterTypeEnumMap = Maps.newHashMap();

            if (typeEnum == ResourcesTypeEnum.LEARNING_RESOURCES) {
                List<String> defaultResourceNames = typeEnum.getDefaultResourceName(chapterTypeEnum);
                defaultResourceNames.add(ResourcesTypeEnum.EXPERIMENT_VIDEO.getMessage());
                courseTypeEnumMap.put(typeEnum, defaultResourceNames);
                chapterTypeEnumMap.put(typeEnum, defaultResourceNames);
            }
            if (typeEnum == ResourcesTypeEnum.OTHER_RESOURCES) {
                courseTypeEnumMap.put(typeEnum, null);
                chapterTypeEnumMap.put(typeEnum, names);
            }

            Map<Long, List<ResourceAuthorityDTO>> courseAuthorityMap = queryResourceAuthority(
                    teacherUserId, courseTypeEnumMap, AuthorityOperatingTypeEnum.COURSE, Lists.newArrayList(courseId));
            Map<Long, List<ResourceAuthorityDTO>> chapterAuthorityMap = queryResourceAuthority(
                    teacherUserId, chapterTypeEnumMap, AuthorityOperatingTypeEnum.CHAPTER, taskIds);

            List<ResourceAuthorityDTO> courseAuthorityList = courseAuthorityMap.get(courseId);

            Map<String, Boolean> courseRemarkMap = Maps.newHashMap();
            if (CollectionUtils.isNotEmpty(courseAuthorityList) && typeEnum == ResourcesTypeEnum.LEARNING_RESOURCES) {
                courseRemarkMap = courseAuthorityList.stream()
                        .collect(Collectors.toMap(
                                ResourceAuthorityDTO::getRemark, ResourceAuthorityDTO::getDownloaded));
            }
            if (CollectionUtils.isNotEmpty(courseAuthorityList) && typeEnum == ResourcesTypeEnum.OTHER_RESOURCES) {
                for (String name : names) {
                    courseRemarkMap.put(name, courseAuthorityList.get(0).getDownloaded());
                }
            }

            Map<String, Boolean> finalCourseRemarkMap = courseRemarkMap;
            resourceInfoDTO.getDisplayVersion().forEach(resourceDTO -> {

                String name = resourceDTO.getName();
                if (StringUtils.contains(name, ResourcesTypeEnum.EXPERIMENT_VIDEO.getMessage())) {
                    name = ResourcesTypeEnum.EXPERIMENT_VIDEO.getMessage();
                }

                resourceDTO.setFilePath(null);
                Boolean courseDownloadFlag = finalCourseRemarkMap.get(name);
                Boolean download = courseDownloadFlag;
                if (courseDownloadFlag == null) {
                    if (userInfoHolder.isStudent()
                            && typeEnum == ResourcesTypeEnum.LEARNING_RESOURCES
                            && ResourcesTypeEnum.getStudentShieldContent().contains(name)) {
                        download = false;
                    } else {
                        download = true;
                    }
                }

                final String finalName = name;
                List<ResourceAuthorityDTO> resourceAuthorityDTOS = chapterAuthorityMap.get(resourceDTO.getChapterId());
                Boolean chapterDownloadFlag = null;
                if (CollectionUtils.isNotEmpty(resourceAuthorityDTOS)) {
                    List<Boolean> chapterDownload = resourceAuthorityDTOS.stream()
                            .filter(resourceAuthorityDTO ->
                                    Objects.equals(resourceDTO.getType(), resourceAuthorityDTO.getResourceType())
                                            && Objects.equals(finalName, resourceAuthorityDTO.getRemark()))
                            .map(ResourceAuthorityDTO::getDownloaded)
                            .collect(Collectors.toList());
                    if (CollectionUtils.isNotEmpty(chapterDownload)) {
                        download = chapterDownload.get(0);
                        chapterDownloadFlag = download;
                    }

                }

                resourceDTO.setTeacherOpenSwitch(download);

                if (userInfoHolder.isStudent() && !download) {
                    resourceDTO.setDownload(download);
                }
                if (userInfoHolder.isTeacher() && typeEnum == ResourcesTypeEnum.LEARNING_RESOURCES
                        && ResourcesTypeEnum.getStudentShieldContent().contains(name)
                        && courseDownloadFlag == null && chapterDownloadFlag == null) {
                    resourceDTO.setTeacherOpenSwitch(false);
                }
            });

            resourceInfoDTO.setDisplayVersion(resourceInfoDTO.getDisplayVersion());

        }

        // 高校教师或者课程下的学生的教师是高校教师 下载隐藏
        if (universityFlag && CollectionUtils.isNotEmpty(names)) {
            List<ResourceDTO> data = resourceInfoDTO.getDisplayVersion();
            data.forEach(resourceDTO -> {
                resourceDTO.setDownload(false);
                resourceDTO.setTeacherOpenSwitch(false);
                if (typeEnum == ResourcesTypeEnum.LEARNING_RESOURCES
                        || typeEnum == ResourcesTypeEnum.OTHER_RESOURCES) {
                    resourceDTO.setFilePath(null);
                }
            });
            resourceInfoDTO.setDisplayVersion(data);
        }

        return resourceInfoDTO;
    }

    private List<ChapterBasicDTO> convertChapter(List<ChapterBasicInfoDTO> chapterBasics, Long courseId) {
        List<ChapterBasicDTO> data = Lists.newArrayList();
        chapterBasics.forEach(chapterBasicInfoDTO -> {
            if (Objects.equals(chapterBasicInfoDTO.getFalg(), 0)) {
                ChapterBasicDTO result = new ChapterBasicDTO();
                result.setId(chapterBasicInfoDTO.getId());
                result.setName(chapterBasicInfoDTO.getName());
                result.setType(chapterBasicInfoDTO.getType());
                result.setIntroduction(chapterBasicInfoDTO.getIntroduction());
                result.setCourseId(courseId);
                result.setOrderNumber(chapterBasicInfoDTO.getOrderNumber());
                result.setFalg(chapterBasicInfoDTO.getFalg());
                result.setParentId(chapterBasicInfoDTO.getParentId());
                if (ChapterTypeEnum.isContainsExperiment(chapterBasicInfoDTO.getType())) {
                    result.setStepTypeStr(StepTypeEnum.getMessageByCode(chapterBasicInfoDTO.getStepType()));
                }
                data.add(result);
            }
        });
        return data;
    }

    private List<ChapterBasicDTO> convert(
            UserInfoHolder userInfoHolder, List<ChapterBasicInfoDTO> chapterBasics, Long courseId) {

        List<ChapterBasicDTO> data = convertChapter(chapterBasics, courseId);
        if (CollectionUtils.isEmpty(data)) throw new NotFoundException("");

        // 获取章节分类并转换为最终结果集
        List<ChapterBasicDTO> courseSortData = convertCourseSort(courseId);

        // 获取章节id，为了根据日志表进行查学习记录做准备
        List<Long> chapterIds = data.stream().map(ChapterBasicDTO::getId).distinct().collect(Collectors.toList());

        // 获取已经学习的章节id集合
        List<Long> studiedChapterIds = operationService.queryOperationIds(
                userInfoHolder.getUserId(), chapterIds, EventCodeEnum.CHAPTER_STUDIED);

        // 获取最近学习的章节id
        OperationDTO operationDTO = getChapterRecording(courseId);

        // 查看当前登录人的课程，是否有正在上课的数据
        UserCourseStatusEnum userCourseStatusEnum = courseService.queryUserCourseStatus(courseId);

        // 学生身份 是否评价
        Map<Long, PaperStatusEnum> chapterPracticeMap = null;
        if (userInfoHolder.isStudent()) {
            chapterPracticeMap = practiceService.getChapterPracticeMap(chapterIds, courseId);
        }

        // 随堂练习是否存在
        Map<Long, Boolean> practiceExistMap = questionRepositoryService.getExistMap(chapterIds);

        // 课程的专业方向类型（本科、专科）
        List<SchoolTypeEnum> courseSchoolTypeEnums = getSchoolTypes(courseId);

        // 转换结果集
        return convertChapterBasic(ChapterConvertTypeEnum.CHAPTER, data, userCourseStatusEnum, studiedChapterIds,
                practiceExistMap, chapterPracticeMap, courseSortData, operationDTO, courseSchoolTypeEnums);
    }

    private List<SchoolTypeEnum> getSchoolTypes(Long courseId) {

        List<SystemConfig> configs = configService.listConfigByKeys(
                Lists.newArrayList(SystemConfigEnum.SCHOOL_TYPE.getKey()));
        if (CollectionUtils.isEmpty(configs)) return null;

        List<CourseExtend> courseExtends = directionService.queryCourseExtend(
                null, Lists.newArrayList(courseId));
        if (CollectionUtils.isEmpty(courseExtends)) return null;

        // 获取专业方向
        List<Long> directionIds = courseExtends.stream().map(CourseExtend::getDirectionId).collect(Collectors.toList());
        List<Direction> directions = directionService.queryDirection(directionIds);
        if (CollectionUtils.isEmpty(directions)) return null;

        List<Integer> types = directions.stream().map(Direction::getType).distinct().collect(Collectors.toList());
        return configs.stream()
                .filter(systemConfig -> types.contains(systemConfig.getId()))
                .map(systemConfig -> SchoolTypeEnum.getEnum(systemConfig.getKeyValue()))
                .collect(Collectors.toList());

    }

    private OperationDTO getChapterRecording(Long courseId) {
        try {
            OperationDTO chapterRecording = operationService.getChapterRecording(userInfoHolder.getUserId(), courseId);
            return chapterRecording;
        } catch (Exception e) {
            return null;
        }
    }

    private List<ChapterBasicDTO> convertCourseSort(Long courseId) {

        // 获取课程分类
        List<CourseSort> courseSorts = courseService.queryCourseSort(Lists.newArrayList(courseId));

        if (CollectionUtils.isEmpty(courseSorts)) return Lists.newArrayList();

        List<Long> courseSortIds = courseSorts.stream().map(CourseSort::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(courseSortIds)) return Lists.newArrayList();
        List<ChapterCourseSort> chapterCourseSorts = queryChapterCourseSort(courseSortIds, null);
        if (CollectionUtils.isEmpty(chapterCourseSorts)) return Lists.newArrayList();

        Map<Long, List<ChapterCourseSort>> courseSortIdMap = BeanUtil.aggByKeyToList(
                "courseSortId", chapterCourseSorts);

        List<ChapterBasicDTO> data = Lists.newArrayList();
        for (CourseSort courseSort : courseSorts) {
            if (MapUtils.isEmpty(courseSortIdMap) || CollectionUtils.isEmpty(courseSortIdMap.get(courseSort.getId()))) {
                continue;
            }
            ChapterBasicDTO chapterBasicDTO = new ChapterBasicDTO();
            chapterBasicDTO.setId(courseSort.getId());
            chapterBasicDTO.setName(courseSort.getName());
            chapterBasicDTO.setLevels(3);
            chapterBasicDTO.setChapterIds(courseSortIdMap.get(courseSort.getId()).stream()
                    .map(ChapterCourseSort::getChapterId).collect(Collectors.toList()));
            data.add(chapterBasicDTO);
        }
        return data;
    }

    private List<ChapterDTO> convertChapterOtherInfo(
            ChapterBasicDTO chapterBasicDTO, ChapterTypeEnum chapterTypeEnum, List<ResourceRelationDTO> resourceDTOS,
            UserCourseStatusEnum userCourseStatusEnum, Map<Long, Boolean> practiceExistMap,
            Map<Long, PaperStatusEnum> chapterPracticeMap, boolean studentIsTesting,
            Map<Long, List<ChapterBasicDTO>> childrenChapterMap, OperationDTO operationDTO,
            List<SchoolTypeEnum> courseSchoolTypeEnums) {

        Long chapterId = chapterBasicDTO.getId();
        boolean manyExperiment = ChapterTypeEnum.isManyExperiment(chapterBasicDTO.getType());
        boolean containsExperiment = ChapterTypeEnum.isContainsExperiment(chapterBasicDTO.getType());
        List<ChapterBasicDTO> childrenChapters = childrenChapterMap.get(chapterId);

        // 定义结果集
        List<ChapterDTO> result = Lists.newArrayList();

        // 根据用户角色获取章节展示的内容
        List<ChapterResourceViewEnum> chapterResourceViewEnums = chapterTypeEnum.getChapterResourceView(
                IdentityEnum.getByRole(userInfoHolder.getUser().getRole()));

        // 对章节展示的内容进行赋值，查看是否置灰、文件是否存在、评价等信息
        for (ChapterResourceViewEnum viewEnum : chapterResourceViewEnums) {

            String name = viewEnum.getMessage();

            if (viewEnum == ChapterResourceViewEnum.THEORY && CollectionUtils.isNotEmpty(courseSchoolTypeEnums)
                    && !courseSchoolTypeEnums.contains(SchoolTypeEnum.BACHELOR_DEGREE)
                    && courseSchoolTypeEnums.contains(SchoolTypeEnum.COLLEGE_DEGREE)) {
                name = viewEnum.getName(CodeEnum.COLLEGE_DEGREE);
            }
            if (viewEnum == ChapterResourceViewEnum.EXPERIMENT && containsExperiment) {
                if (CollectionUtils.isEmpty(childrenChapters)) continue;
                for (int i = 0; i < childrenChapters.size(); i++) {
                    ChapterBasicDTO childrenChapter = childrenChapters.get(i);
                    ChapterDTO chapterDTO = new ChapterDTO(
                            viewEnum.getCode(), viewEnum.getMessage(), childrenChapter.getId(), chapterId);
                    if (manyExperiment) {
                        chapterDTO.setName(String.format(viewEnum.getName(CodeEnum.MANY_EXPERIMENT),
                                String.format("%02d", i + 1), childrenChapter.getName()));
                    }
                    setChapterDTO(chapterDTO, viewEnum, resourceDTOS, userCourseStatusEnum,
                            practiceExistMap, chapterPracticeMap, studentIsTesting,
                            childrenChapter.getId(), operationDTO);
                    chapterDTO.setExperimentCourseFlag(true);
                    // 需求：若数据为暂无资源，则不进行显示
                    if (Objects.equals(chapterDTO.getHideDescription(), "暂无资源")) continue;
                    result.add(chapterDTO);
                }
                continue;
            }

            ChapterDTO chapterDTO = new ChapterDTO(viewEnum.getCode(), name, chapterId, chapterId);
            setChapterDTO(chapterDTO, viewEnum, resourceDTOS, userCourseStatusEnum, practiceExistMap,
                    chapterPracticeMap, studentIsTesting, chapterId, operationDTO);
            // 需求：若数据为暂无资源，则不进行显示
            if (Objects.equals(chapterDTO.getHideDescription(), "暂无资源")) continue;
            result.add(chapterDTO);

        }
        return result;
    }

    private ChapterDTO setChapterDTO(
            ChapterDTO chapterDTO, ChapterResourceViewEnum viewEnum, List<ResourceRelationDTO> resourceDTOS,
            UserCourseStatusEnum userCourseStatusEnum, Map<Long, Boolean> practiceExistMap,
            Map<Long, PaperStatusEnum> chapterPracticeMap, boolean studentIsTesting,
            Long chapterId, OperationDTO operationDTO) {

        // 先判断权限
        if (userCourseStatusEnum == UserCourseStatusEnum.EXIST_CLASS
                || ((userCourseStatusEnum == UserCourseStatusEnum.NOT_CREATE_CLASS
                || userCourseStatusEnum == UserCourseStatusEnum.FORBIDDEN)
                && viewEnum != ChapterResourceViewEnum.RESOURCE)) {
            chapterDTO.setShow(true);

            // 判断文件是否存在
            if (viewEnum == ChapterResourceViewEnum.EXPERIMENT
                    && userCourseStatusEnum != UserCourseStatusEnum.FORBIDDEN) {
                chapterDTO.setResourceExist(true);
            } else if (viewEnum == ChapterResourceViewEnum.PRACTICE) {
                chapterDTO.setResourceExist(false);
                if (chapterId != null && MapUtils.isNotEmpty(practiceExistMap)) {
                    chapterDTO.setResourceExist(practiceExistMap.get(chapterId));
                }
            } else {
                List<Integer> types = viewEnum.getResourcesTypeEnums().stream()
                        .map(resourcesTypeEnum -> resourcesTypeEnum.getCode()).collect(Collectors.toList());
                if (viewEnum == ChapterResourceViewEnum.RESOURCE) {
                    types.add(ResourcesTypeEnum.EXPERIMENT_VIDEO.getCode());
                }
                chapterDTO.setResourceExist(true);
                if (CollectionUtils.isEmpty(resourceDTOS)
                        || (CollectionUtils.isNotEmpty(resourceDTOS) && resourceDTOS.stream()
                        .filter(resourceRelationDTO -> types.contains(resourceRelationDTO.getType())
                                && FileUtil.exist(LocalResourcesEnum.CHAPTER.getLocalUploadPath(
                                new String[]{resourceRelationDTO.getRemark()}, resourceRelationDTO.getFileName()))
                        ).count() == 0)) {
                    chapterDTO.setResourceExist(false);
                }
            }
            if (!chapterDTO.getResourceExist()) {
                chapterDTO.setShow(false);
                chapterDTO.setHideDescription("暂无资源");
            }
        } else {
            chapterDTO.setShow(false);
            chapterDTO.setHideDescription(userCourseStatusEnum.getMessage());
        }

        // 评价
        if (viewEnum == ChapterResourceViewEnum.PRACTICE
                && chapterId != null && MapUtils.isNotEmpty(chapterPracticeMap)) {
            PaperStatusEnum paperStatusEnum = chapterPracticeMap.get(chapterId);
            if (paperStatusEnum == PaperStatusEnum.READ) {
                chapterDTO.setEvaluatStatus("已评价");
            }
            if (paperStatusEnum == PaperStatusEnum.READING) {
                chapterDTO.setEvaluatStatus("已提交");
            }
        }

        // 学生是否考试
        if (studentIsTesting) {
            chapterDTO.setShow(false);
            chapterDTO.setHideDescription("正在考试");
        }

        // 设置已学习标识
        if (operationDTO != null && Objects.equals(operationDTO.getId(), chapterDTO.getId())
                && StringUtils.isNotBlank(operationDTO.getRemark())
                && Objects.equals(Integer.valueOf(operationDTO.getRemark()), viewEnum.getCode())) {
            chapterDTO.setRecentlyStudiedFlag(1);
        }

        return chapterDTO;
    }

    private List<Long> filterOccupyResourceIds(List<Long> resourceIds, List<Long> objIds,
                                               ResourceRelationTypeEnum resourceRelationTypeEnum) {
        List<Long> occupyResourceIds = getOccupyResourceIds(resourceIds, objIds, resourceRelationTypeEnum);
        if (CollectionUtils.isNotEmpty(occupyResourceIds)) {
            resourceIds.removeAll(occupyResourceIds);
        }
        return resourceIds;
    }

    private List<Long> getOccupyResourceIds(List<Long> resourceIds, List<Long> objIds,
                                            ResourceRelationTypeEnum resourceRelationTypeEnum) {
        if (CollectionUtils.isEmpty(resourceIds) || CollectionUtils.isEmpty(objIds)) return Lists.newArrayList();
        ResourceRelationExample example = new ResourceRelationExample();
        example.createCriteria()
                .andTypeEqualTo(resourceRelationTypeEnum.getCode())
                .andResourceIdIn(resourceIds).andObjIdNotIn(objIds);
        List<ResourceRelation> resources = resourceService.queryResourceRelation(example);
        if (CollectionUtils.isEmpty(resources)) return Lists.newArrayList();

        return resources.stream().map(ResourceRelation::getResourceId).collect(Collectors.toList());
    }

    private List<Long> filterOccupyChapterBasic(List<Long> courseIds, List<Long> chapterBasicIds) {
        ChapterExample example = new ChapterExample();
        example.createCriteria()
                .andDeletedEqualTo(false)
                .andCourseIdNotIn(courseIds)
                .andChapterBasicIdIn(chapterBasicIds);
        List<Chapter> chapters = chapterMapper.selectByExample(example);

        if (CollectionUtils.isEmpty(chapters)) return chapterBasicIds;

        List<Long> existChapterBasicIds = chapters.stream()
                .map(Chapter::getChapterBasicId).distinct().collect(Collectors.toList());

        chapterBasicIds.removeAll(existChapterBasicIds);
        return chapterBasicIds;
    }

    private void deleteChapterAndChapterBasic(List<Long> chapterIds, List<Long> chapterBasicIds, Boolean logicDeleted) {

        if (CollectionUtils.isNotEmpty(chapterIds)) {
            Chapter chapter = new Chapter();
            chapter.setDeleted(true);
            ChapterExample example = new ChapterExample();
            example.createCriteria().andIdIn(chapterIds);
            if (logicDeleted) {
                chapterMapper.updateByExampleSelective(chapter, example);
            } else {
                chapterMapper.deleteByExample(example);
            }
        }

        if (CollectionUtils.isNotEmpty(chapterBasicIds)) {
            ChapterBasic basic = new ChapterBasic();
            basic.setDeleted(true);
            ChapterBasicExample basicExample = new ChapterBasicExample();
            basicExample.createCriteria().andIdIn(chapterBasicIds);
            if (logicDeleted) {
                chapterBasicMapper.updateByExampleSelective(basic, basicExample);
            } else {
                chapterBasicMapper.deleteByExample(basicExample);
            }
        }

    }

    private void checkOrderAdjustmentsChapter(Long courseId, List<Long> chapterIds) {
        List<ZtreeDTO> chapterOwner = getChapterOwner(courseId);
        if (CollectionUtils.isEmpty(chapterOwner)) {
            throw new NotFoundException("暂无数据，无法修改");
        }

        List<ZtreeDTO> children = chapterOwner.get(0).getChildren();
        if (CollectionUtils.isEmpty(children)) {
            throw new NotFoundException("暂无数据，无法修改");
        }

        List<Long> dbChapterIds = children.stream().map(ZtreeDTO::getId).collect(Collectors.toList());

        if (!CollectionUtils.isEqualCollection(dbChapterIds, chapterIds)) {
            throw new NotFoundException("章节数据有更新，请重新刷新后再调整章节顺序");
        }
    }

    private Chapter convertChapter(Long courseId) {
        // 创建章节表
        Chapter chapter = new Chapter();
        chapter.setCourseId(courseId);
        chapter.setOrderNumber(getOrderNumber(courseId));
        return chapter;
    }

    private Integer getOrderNumber(Long courseId) {
        // 获取序号
        List<Chapter> dbChapterIds = getChapter(courseId, ChapterEnum.CHAPTER);
        return CollectionUtils.isEmpty(dbChapterIds) ?
                1 : dbChapterIds.get(dbChapterIds.size() - 1).getOrderNumber() + 1;
    }

    private void updateResource(
            Long courseId, Long chapterId, Integer paramType, Integer dbType, Boolean logicDeleted) {

        /**
         * 往下降类型，需要删除内容：
         * 1. 删除资源、章节资源关联表
         * 2. 删除资源权限
         * 3. 删除笔记
         * 4. 删除随堂练习
         * 5. 随堂提问
         * 6. 删除虚机、虚机配置
         */
        List<ChapterTypeEnum> deleteTypeEnums = getDeleteTypeEnum(paramType, dbType);
        if (CollectionUtils.isEmpty(deleteTypeEnums)) return;

        // 获取要删除资源的类型
        List<Integer> resourceTypes = Lists.newArrayList();
        for (ChapterTypeEnum deleteTypeEnum : deleteTypeEnums) {
            resourceTypes.addAll(deleteTypeEnum.getResourceType().stream()
                    .filter(typeEnum -> typeEnum != ResourcesTypeEnum.OTHER_RESOURCES)
                    .map(ResourcesTypeEnum::getCode).collect(Collectors.toList()));
        }
        resourceTypes = resourceTypes.stream().distinct().collect(Collectors.toList());

        // 获取章节/任务
        List<ChapterBasicInfoDTO> chapters = listChapterInfo(Lists.newArrayList(courseId), null, null);
        chapters = filterChapterAndTask(chapters, Lists.newArrayList(chapterId));
        if (CollectionUtils.isEmpty(chapters)) return;

        List<Long> chapterIds = chapters.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());

        // 删除随堂练习
        if (resourceTypes.contains(ResourcesTypeEnum.PRACTICE.getCode())) {
            questionRepositoryService.deleteQuestion(Lists.newArrayList(chapterId), QuestionBusinessTypeEnum.PRACTICE);
            practiceService.deletePractice(Lists.newArrayList(chapterId));
        }

        // 删除随堂提问
        if (deleteTypeEnums.contains(ChapterTypeEnum.THEORY)) {
            quizManageService.deleteQuizByChapterIds(Lists.newArrayList(chapterId));
        }

        // 删除容器、镜像分配
        if (deleteTypeEnums.contains(ChapterTypeEnum.EXPERIMENT)) {
            deleteContainerAndImage(chapterIds, logicDeleted, null);
        }

        // 删除资源、笔记
        deleteResourceAndOther(
                chapters.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList()),
                deleteTypeEnums, resourceTypes);

        // 删除任务chapter及chapter_basic信息
        if (deleteTypeEnums.contains(ChapterTypeEnum.EXPERIMENT)) {
            chapters = ChapterEnum.TASK.getConvertData(chapters);
            List<Long> taskIds = chapters.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(taskIds)) {
                deleteChapter(Lists.newArrayList(courseId), taskIds, true, true, null);
            }
        }

    }

    private void deleteResourceAndOther(
            List<Long> chapterIds, List<ChapterTypeEnum> deleteTypeEnums, List<Integer> resourceTypes) {

        // 根据章节id集合获取资源信息
        List<ResourceRelationDTO> data = resourceService.getResourceRelationInfo(
                chapterIds, resourceTypes, null, ResourceRelationTypeEnum.CHAPTER.getCode());

        // 将数据库的数据进行过滤成想要的资源数据
        List<ResourceRelationDTO> resourceRelationDTOS = convertResourceRelationDTO(data, deleteTypeEnums);

        if (CollectionUtils.isEmpty(resourceRelationDTOS)) return;

        List<Long> resourceRelationIds = resourceRelationDTOS.stream()
                .map(ResourceRelationDTO::getResourceRelationId).collect(Collectors.toList());
        List<Long> resourceIds = resourceRelationDTOS.stream()
                .map(ResourceRelationDTO::getResourceId).collect(Collectors.toList());
        List<Long> filterResourceIds = filterOccupyResourceIds(resourceIds, chapterIds,
                ResourceRelationTypeEnum.CHAPTER);
        resourceRelationDTOS = resourceRelationDTOS.stream()
                .filter(resourceRelationDTO -> filterResourceIds.contains(resourceRelationDTO.getResourceId()))
                .collect(Collectors.toList());

        // 删除章节资源表数据
        resourceService.deleteResourceRelation(resourceRelationIds);

        // 删除笔记，并获取笔记静态资源
        List<NotesTypeEnum> notesTypeEnums = Lists.newArrayList();
        if (deleteTypeEnums.contains(ChapterTypeEnum.EXPERIMENT)) {
            notesTypeEnums.add(NotesTypeEnum.EXPERIMENT_NOTES);
        }
        if (deleteTypeEnums.contains(ChapterTypeEnum.THEORY)) {
            notesTypeEnums.addAll(Lists.newArrayList(NotesTypeEnum.COURSEWARE_NOTES,
                    NotesTypeEnum.PREPARATION_MATERIALS_NOTES));
        }
        List<String> notesPaths = deleteNotes(chapterIds, null, notesTypeEnums);

        // 删除数据库资源表
        if (CollectionUtils.isNotEmpty(filterResourceIds)) {
            resourceService.deleteBatch(filterResourceIds);
        }

        // 获取资源是否被其他类型所占用
        List<Resource> existResources = getExistResource(resourceRelationDTOS);
        Map<String, List<String>> fileNameMap = existResources.stream()
                .collect(Collectors.groupingBy(Resource::getFileName,
                        Collectors.mapping(Resource::getRemark, Collectors.toList())));

        // 静态资源
        deleteStaticResources(notesPaths);
        deleteStaticResources(resourceRelationDTOS.stream()
                .filter(resourceRelationDTO -> StringUtils.isNotBlank(resourceRelationDTO.getFileName())
                        && (CollectionUtils.isEmpty(fileNameMap.get(resourceRelationDTO.getFileName()))
                        || !fileNameMap.get(resourceRelationDTO.getFileName()).contains(resourceRelationDTO.getRemark())
                )).map(resourceRelationDTO -> LocalResourcesEnum.CHAPTER.getLocalUploadPath(
                        new String[]{resourceRelationDTO.getRemark()},
                        resourceRelationDTO.getFileName())
                ).collect(Collectors.toList()));
    }

    private List<ResourceRelationDTO> convertResourceRelationDTO(
            List<ResourceRelationDTO> data, List<ChapterTypeEnum> deleteTypeEnums) {
        List<ResourceRelationDTO> resourceRelationDTOS = Lists.newArrayList();
        for (ResourceRelationDTO resourceRelationDTO : data) {
            // 如果不是学习资源添加到结果集
            if (!Objects.equals(resourceRelationDTO.getType(),
                    ResourcesTypeEnum.LEARNING_RESOURCES.getCode())) {
                resourceRelationDTOS.add(resourceRelationDTO);
                continue;
            }
            // 课程材料进行过滤
            if (Objects.equals(resourceRelationDTO.getName(), ResourcesTypeEnum.COURSE_MATERIAL.getMessage())) {
                continue;
            }

            List<String> theorys = ResourcesTypeEnum.LEARNING_RESOURCES.getDefaultResourceName(ChapterTypeEnum.THEORY);
            List<String> experiments = ResourcesTypeEnum.LEARNING_RESOURCES
                    .getDefaultResourceName(ChapterTypeEnum.EXPERIMENT);
            if (deleteTypeEnums.contains(ChapterTypeEnum.THEORY) && theorys.contains(resourceRelationDTO.getName())) {
                resourceRelationDTOS.add(resourceRelationDTO);
            } else if (deleteTypeEnums.contains(ChapterTypeEnum.EXPERIMENT)
                    && experiments.contains(resourceRelationDTO.getName())) {
                resourceRelationDTOS.add(resourceRelationDTO);
            }
        }
        return resourceRelationDTOS;
    }

    private List<ChapterTypeEnum> getDeleteTypeEnum(Integer paramType, Integer dbType) {

        ChapterTypeEnum dbTypeEnum = ChapterTypeEnum.getByCode(dbType);
        ChapterTypeEnum paramTypeEnum = ChapterTypeEnum.getByCode(paramType);

        // 如果之前的章节类型与参数章节类型一致
        if (paramTypeEnum == dbTypeEnum) return null;

        List<ChapterTypeEnum> deleteChapterTypes = Lists.newArrayList();

        boolean paramTheory = ChapterTypeEnum.isContainsTheory(paramType);
        boolean dbTheory = ChapterTypeEnum.isContainsTheory(dbType);

        boolean paramExperiment = ChapterTypeEnum.isContainsExperiment(paramType);
        boolean dbExperiment = ChapterTypeEnum.isContainsExperiment(dbType);

        boolean paramManyExperiment = ChapterTypeEnum.isManyExperiment(paramType);
        boolean dbManyExperiment = ChapterTypeEnum.isManyExperiment(dbType);

        // 数据库包含理论课，参数不包含，则删除理论课
        if (dbTheory && !paramTheory) {
            deleteChapterTypes.add(ChapterTypeEnum.THEORY);
        }
        // 数据库包含实验课，参数不包含，则删除实验课。注意：这个判断不需要区分是单实验还是多实验
        if (dbExperiment && !paramExperiment) {
            deleteChapterTypes.add(ChapterTypeEnum.EXPERIMENT);
        }
        // 数据库与参数都包含实验课，但参数实验课和数据库实验课类型不一致，则删除实验课
        if (dbExperiment && paramExperiment && paramManyExperiment != dbManyExperiment) {
            deleteChapterTypes.add(ChapterTypeEnum.EXPERIMENT);
        }
        return deleteChapterTypes;
    }

    private ChapterBasicInfoDTO checkAndGetChapter(Long courseId, Long chapterId, ChapterParam chapterParam) {
        // 检查章节是否存在
        ChapterBasicInfoDTO data = checkAndGetChapter(courseId, chapterId);

        // 检查数据
        checkChapterParam(chapterParam);

        return data;
    }

    private CourseDTO checkAndGetCourse(Long courseId) {
        List<CourseDTO> courseDTOS = courseService.listCourse(null, null, null, null,
                Lists.newArrayList(courseId));
        if (CollectionUtils.isEmpty(courseDTOS)) {
            throw new GlobalException("");
        }
        return courseDTOS.get(0);
    }

    private void copyNotes(List<Long> userIds, Long courseId, Map<Long, Long> chapterIdMap) {
        List<ClassDTO> classDTOS = classService.listClassCourse(
                courseId, null, null, false, null);
        if (CollectionUtils.isNotEmpty(classDTOS)) {
            // 获取当前课程下的未结课的学生信息
            List<Long> classIds = classDTOS.stream().map(ClassDTO::getId).distinct().collect(Collectors.toList());
            List<UserInfoDTO> userInfoDTOS = userService.queryClassUser(classIds, null);
            if (CollectionUtils.isNotEmpty(userInfoDTOS)) {
                userIds.addAll(userInfoDTOS.stream().map(UserInfoDTO::getId).collect(Collectors.toList()));
            }
        }

        // 获取用户的笔记
        NotesExample example = new NotesExample();
        example.createCriteria().andUserIdIn(userIds).andChapterIdIn(Lists.newArrayList(chapterIdMap.keySet()));
        List<Notes> notes = notesMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(notes)) return;

        // 转换参数
        List<NotesDTO> notesDTOS = BeanUtil.batchTransform(NotesDTO.class, notes);
        notesDTOS.forEach(notesDTO -> {
            Long newChapterId = chapterIdMap.get(notesDTO.getChapterId());
            if (newChapterId != null) {
                notesDTO.setUpdateChapterId(newChapterId);
            }
        });

        // 修改笔记
        notesMapperExt.updateBatch(notesDTOS);
    }

    private ChapterBasicInfoDTO checkAndGetChapter(Long courseId, Long chapterId) {
        List<ChapterBasicInfoDTO> chapterData = listChapterInfo(
                Lists.newArrayList(courseId), Lists.newArrayList(chapterId), null);
        if (CollectionUtils.isEmpty(chapterData)) {
            throw new BadRequestException("暂无章节");
        }
        return chapterData.get(0);
    }

    private List<UpdateResourceAuthorityParam> checkAndGetParam(
            Long courseId, List<Long> chapterIds, List<UpdateResourceAuthorityParam> params,
            AuthorityOperatingTypeEnum typeEnum) {
        // 检查参数
        if (CollectionUtils.isEmpty(params) || typeEnum == null ||
                (courseId == null && CollectionUtils.isEmpty(chapterIds))) {
            throw new BadRequestException();
        }
        if ((typeEnum == AuthorityOperatingTypeEnum.COURSE && courseId == null)
                || (typeEnum == AuthorityOperatingTypeEnum.CHAPTER && CollectionUtils.isEmpty(chapterIds))) {
            throw new BadRequestException();
        }

        params.forEach(param -> {
            ResourcesTypeEnum byCode = ResourcesTypeEnum.getByCode(param.getResourcesType());
            if (byCode == null) {
                throw new BadRequestException();
            }
            if (param.getAllResources()) {
                if (param.getDownload() == null && param.getShow() == null) {
                    throw new BadRequestException();
                }
                return;
            }

            List<PermissionParam> permission = param.getPermission();
            if (permission.stream().filter(permissionParam -> permissionParam.getDownload() == null
                    && permissionParam.getShow() == null).count() > 0) {
                throw new BadRequestException();
            }

            // 若是教学资源，需要更改实验视频名称
            if (byCode == ResourcesTypeEnum.LEARNING_RESOURCES) {
                for (PermissionParam permissionParam : param.getPermission()) {
                    if (StringUtils.contains(permissionParam.getName(),
                            ResourcesTypeEnum.EXPERIMENT_VIDEO.getMessage())) {
                        permissionParam.setName(ResourcesTypeEnum.EXPERIMENT_VIDEO.getMessage());
                    }
                }
            }

        });

        // 检查章节是否存在
        if (typeEnum == AuthorityOperatingTypeEnum.CHAPTER) {
            List<ChapterBasicInfoDTO> data = listChapterInfo(Lists.newArrayList(courseId), chapterIds, null);
            if (CollectionUtils.isEmpty(data)) {
                throw new BadRequestException();
            }
            List<Long> dbChapterIds = data.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
            if (chapterIds.stream().filter(chapterId -> !dbChapterIds.contains(chapterId)).count() > 0) {
                throw new BadRequestException();
            }
        }

        return params;
    }

    private void deleteContainerAndImage(List<Long> experimentChapterIds,
                                         Boolean logicDeleted, Boolean updatePreseted) {

        if (CollectionUtils.isEmpty(experimentChapterIds)) return;

        if (logicDeleted) {
            experimentChapterIds.forEach(chapterId -> {
                // 删除课程或章节时删除容器
                containerService.deleteContainerByContext(ContextTypeEnum.TASK.getCode(), chapterId);

                if (updatePreseted == null || !updatePreseted) {
                    // 删除课程或章节时删除镜像分配
                    imageAllocationService.deleteImageAllocation(ContextTypeEnum.TASK.getCode(), chapterId);
                }

            });
            return;
        }
        experimentChapterIds.forEach(chapterId -> {
            // 删除课程或章节时删除容器
            containerService.trueDeleteContainerByContext(ContextTypeEnum.TASK.getCode(), chapterId);

            if (updatePreseted == null || !updatePreseted) {
                // 删除课程或章节时删除镜像分配
                imageAllocationService.trueDeleteImageAllocation(ContextTypeEnum.TASK.getCode(), chapterId);
            }

        });

    }

    /**
     * 删除笔记，并获取笔记静态资源
     *
     * @param typeEnum
     * @param name
     * @param chapterId
     * @return
     */
    private List<String> deleteNotes(ResourcesTypeEnum typeEnum, String name, Long chapterId) {
        //
        List<NotesTypeEnum> typeEnums = Lists.newArrayList();
        if (typeEnum == ResourcesTypeEnum.COURSEWARE
                || (typeEnum == ResourcesTypeEnum.LEARNING_RESOURCES
                && Objects.equals(name, ResourcesTypeEnum.COURSEWARE.getCode()))) {
            typeEnums.add(NotesTypeEnum.COURSEWARE_NOTES);
        }
        if (typeEnum == ResourcesTypeEnum.PREPARATION_MATERIALS) {
            typeEnums.add(NotesTypeEnum.PREPARATION_MATERIALS_NOTES);
        }
        return deleteNotes(Lists.newArrayList(chapterId), null, typeEnums);
    }

    private List<ResourceAuthority> convertResourceAuthorityList(List<Long> objIds, Long userId,
                                                                 List<UpdateResourceAuthorityParam> params,
                                                                 AuthorityOperatingTypeEnum typeEnum) {
        List<ResourceAuthority> resourceAuthorities = Lists.newArrayList();

        Map<Long, List<UpdateResourceAuthorityParam>> chapterIdMap = null;

        if (typeEnum == AuthorityOperatingTypeEnum.CHAPTER) {
            chapterIdMap = BeanUtil.aggByKeyToList("chapterId", params);
        }

        Map<Long, List<UpdateResourceAuthorityParam>> finalChapterIdMap = chapterIdMap;
        objIds.forEach(objId -> {
            resourceAuthorities.addAll(convertAuthorityList(objId, typeEnum == AuthorityOperatingTypeEnum.CHAPTER ?
                    finalChapterIdMap.get(objId) : params, userId, typeEnum));
        });

        return resourceAuthorities;
    }

    private List<ResourceAuthority> convertAuthorityList(Long objId, List<UpdateResourceAuthorityParam> params,
                                                         Long userId, AuthorityOperatingTypeEnum typeEnum) {
        List<ResourceAuthority> resourceAuthorities = Lists.newArrayList();
        params.forEach(updateResourceAuthorityParam -> {
            Integer resourceType = updateResourceAuthorityParam.getResourcesType();
            if (!updateResourceAuthorityParam.getAllResources()
                    && CollectionUtils.isNotEmpty(updateResourceAuthorityParam.getPermission())) {
                List<PermissionParam> permissions = updateResourceAuthorityParam.getPermission();
                permissions.forEach(permissionParam -> {
                    ResourceAuthority obj = new ResourceAuthority();
                    obj.setUserId(userId);
                    obj.setResourceType(resourceType);
                    obj.setOperatingType(typeEnum.getCode());
                    obj.setObjId(objId);
                    obj.setShowed(permissionParam.getShow());
                    obj.setDownloaded(permissionParam.getDownload());
                    obj.setRemark(permissionParam.getName());
                    resourceAuthorities.add(obj);
                });
                return;
            }
            ResourceAuthority obj = new ResourceAuthority();
            obj.setUserId(userId);
            obj.setResourceType(resourceType);
            obj.setOperatingType(typeEnum.getCode());
            obj.setObjId(objId);
            obj.setShowed(updateResourceAuthorityParam.getShow());
            obj.setDownloaded(updateResourceAuthorityParam.getDownload());
            obj.setRemark("");
            resourceAuthorities.add(obj);
        });
        return resourceAuthorities;
    }

    private ZtreeDTO convertZtreeDTO(List<CourseBasicDTO> courseDTOS, String name) {
        ZtreeDTO ztreeDTO = new ZtreeDTO();
        ztreeDTO.setName(name);
        ztreeDTO.setChildren(convertZtreeDTO(courseDTOS));
        return ztreeDTO;
    }

    private String getFileName(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            return "";
        }
        return fileName.replaceAll(" ", "").replaceAll(",", "")
                .replaceAll("%", "").replaceAll("_", "-");
    }

    private List<Resource> getExistResource(List<ResourceRelationDTO> resourceRelationDTOS) {
        ResourceExample example = new ResourceExample();
        resourceRelationDTOS.forEach(resourceRelationDTO -> {
            ResourceExample.Criteria criteria = example.createCriteria();
            criteria.andIdNotEqualTo(resourceRelationDTO.getResourceId());
            criteria.andFileNameEqualTo(resourceRelationDTO.getFileName());
            criteria.andRemarkEqualTo(resourceRelationDTO.getRemark());
            example.or(criteria);
        });
        return resourceService.queryResource(example);
    }

    private List<String> deleteResource(Long objId, List<Integer> types, String name,
                                        ResourceRelationTypeEnum resourceRelationTypeEnum) {

        List<String> datas = Lists.newArrayList();

        // 查询数据库章节资源的信息
        List<ResourceRelationDTO> resourceRelationDTOS = resourceService.getResourceRelationInfo(
                Lists.newArrayList(objId), types, Lists.newArrayList(name), resourceRelationTypeEnum.getCode());

        if (CollectionUtils.isEmpty(resourceRelationDTOS)) {
            return null;
        }

        // 当数据库无资源 或 资源被人占用 就插入。如果不占用资源 就修改。
        List<Long> resourceIds = resourceRelationDTOS.stream()
                .map(ResourceRelationDTO::getResourceId).collect(Collectors.toList());
        List<Long> occupyResourceIds = getOccupyResourceIds(resourceIds, Lists.newArrayList(objId),
                resourceRelationTypeEnum);

        if (CollectionUtils.isEmpty(occupyResourceIds)) {

            // 删除resources表
            resourceService.deleteBatch(resourceIds);

            // 获取资源是否被其他类型所占用
            List<Resource> existResources = getExistResource(resourceRelationDTOS);
            Map<String, List<String>> fileNameMap = existResources.stream()
                    .collect(Collectors.groupingBy(Resource::getFileName,
                            Collectors.mapping(Resource::getRemark, Collectors.toList())));

            // 删除资源操作
            datas = resourceRelationDTOS.stream()
                    .filter(resourceRelationDTO -> StringUtils.isNotBlank(resourceRelationDTO.getFileName())
                            && (CollectionUtils.isEmpty(fileNameMap.get(resourceRelationDTO.getFileName()))
                            || !fileNameMap.get(resourceRelationDTO.getFileName())
                            .contains(resourceRelationDTO.getRemark())
                    )).map(resourceRelationDTO ->
                            LocalResourcesEnum.CHAPTER.getLocalUploadPath(
                                    new String[]{resourceRelationDTO.getRemark()},
                                    resourceRelationDTO.getFileName())
                    ).collect(Collectors.toList());
        }

        // 删除章节和资源的关联关系
        resourceService.deleteResourceRelation(
                resourceRelationDTOS.stream()
                        .map(ResourceRelationDTO::getResourceRelationId).collect(Collectors.toList())
        );
        return datas;
    }

    private void insertResourceAndRelation(Long courseId, Long objId, ResourceRelationTypeEnum typeEnum,
                                           Map<Integer, List<String>> typeFileNamesMap, String name) {

        // 转换resource结果集
        List<Resource> resources = convertResource(typeFileNamesMap, name, courseId);
        if (CollectionUtils.isEmpty(resources)) {
            throw new GlobalException("");
        }

        // 插入数据库
        resourceService.insertBatch(resources);

        // 转换为ResourceRelation结果集
        List<Long> resourceIds = resources.stream().map(Resource::getId).collect(Collectors.toList());
        List<ResourceRelation> resourceRelations = convertChapterResources(resourceIds, objId, typeEnum);
        if (CollectionUtils.isEmpty(resourceRelations)) {
            throw new GlobalException("");
        }

        // 插入数据库
        resourceService.insertBatchResourceRelation(resourceRelations);

    }

    /**
     * 获取所有章节下的某些章节及章节下的任务
     *
     * @param dbChapters 数据库章节信息
     * @param chapterIds 章节id集合
     * @return
     */
    private List<ChapterBasicInfoDTO> filterChapterAndTask(
            List<ChapterBasicInfoDTO> dbChapters, List<Long> chapterIds) {

        if (CollectionUtils.isEmpty(dbChapters) || CollectionUtils.isEmpty(chapterIds)) return dbChapters;

        // 如果参数中不传章节，就代表删除全部章节，如果传章节，就删除这些章节
        return dbChapters.stream()
                .filter(chapterBasicInfoDTO ->
                        chapterIds.contains(chapterBasicInfoDTO.getId())
                                || (chapterBasicInfoDTO.getParentId() != null
                                && chapterBasicInfoDTO.getParentId() > 0
                                && chapterIds.contains(chapterBasicInfoDTO.getParentId())))
                .collect(Collectors.toList());
    }

    private Map<ResourcesTypeEnum, List<ResourceRelationDTO>> convertResourceTypeMap(
            Map<ResourcesTypeEnum, List<ResourceRelationDTO>> typeMap, ChapterBasicInfoDTO chapterInfo,
            StepTypeEnum stepTypeEnum) {

        // 获取资源的章节id集合
        List<ChapterBasicInfoDTO> tasks = listTask(Lists.newArrayList(chapterInfo.getId()), null);
        List<Long> taskIds = tasks.stream().map(ChapterBasicInfoDTO::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(taskIds)) return typeMap;

        // 获取任务下的步骤
        List<Long> objIds = taskIds;
        Map<Long, List<StepChapter>> chapterIdMap = null;
        if (stepTypeEnum == StepTypeEnum.MULTIPLE) {
            List<StepChapter> stepChapters = stepService.listStepChapter(taskIds);
            if (CollectionUtils.isEmpty(stepChapters)) return typeMap;
            objIds = stepChapters.stream().map(StepChapter::getId).collect(Collectors.toList());
            chapterIdMap = BeanUtil.aggByKeyToList("chapterId", stepChapters);
        }

        // 获取章节id下的实验课视频资源
        List<ResourceRelationDTO> experimentVideos = resourceService.getResourceRelationInfo(
                objIds, Lists.newArrayList(ResourcesTypeEnum.EXPERIMENT_VIDEO.getCode()), null,
                stepTypeEnum == StepTypeEnum.MULTIPLE ?
                        ResourceRelationTypeEnum.STEP.getCode() : ResourceRelationTypeEnum.CHAPTER.getCode());
        if (CollectionUtils.isEmpty(experimentVideos)) return typeMap;

        // 若有实验课视频资源，将原先map数据获取
        ResourcesTypeEnum learningResources = ResourcesTypeEnum.LEARNING_RESOURCES;
        typeMap = MapUtils.isEmpty(typeMap) ? Maps.newHashMap() : typeMap;
        List<ResourceRelationDTO> resourceRelationDTOS = typeMap.get(learningResources);
        resourceRelationDTOS = CollectionUtils.isEmpty(resourceRelationDTOS) ?
                Lists.newArrayList() : resourceRelationDTOS;

        Map<Long, ResourceRelationDTO> resourcesMap = BeanUtil.mapByKey("objId", experimentVideos);

        // 对map集合增加学习资源数据
        for (int i = 0; i < taskIds.size(); i++) {

            Long taskId = taskIds.get(i);

            String name = ResourcesTypeEnum.EXPERIMENT_VIDEO.getMessage();
            String taskName = "";
            if (ChapterTypeEnum.isManyExperiment(chapterInfo.getType())) {
                taskName = "任务" + (i + 1);
            }

            if (stepTypeEnum == StepTypeEnum.MULTIPLE && MapUtils.isNotEmpty(chapterIdMap)) {
                List<StepChapter> stepChapters = chapterIdMap.get(taskId);
                for (int k = 0; k < stepChapters.size(); k++) {
                    StepChapter stepChapter = stepChapters.get(k);
                    ResourceRelationDTO resourceDTO = resourcesMap.get(stepChapter.getId());
                    if (resourceDTO == null) continue;

                    String str = StringUtils.isBlank(taskName) ? "" : taskName + "-";
                    resourceDTO.setName(str + "步骤" + (k + 1) + "-" + name);
                    resourceDTO.setType(learningResources.getCode());
                    resourceRelationDTOS.add(resourceDTO);
                }
            } else {
                ResourceRelationDTO resourceDTO = resourcesMap.get(taskId);
                if (resourceDTO == null) continue;

                String str = StringUtils.isBlank(taskName) ? "" : taskName + "-";
                resourceDTO.setName(str + name);
                resourceDTO.setType(learningResources.getCode());
                resourceRelationDTOS.add(resourceDTO);
            }

        }
        typeMap.put(learningResources, resourceRelationDTOS);
        return typeMap;
    }

    private List<ResourcesParam> convert(List<ResourcesTypeEnum> resourcesTypeEnums, Long chapterId,
                                         StepTypeEnum stepTypeEnum, List<Long> stepChapterIds) {

        if (stepTypeEnum == StepTypeEnum.MULTIPLE && CollectionUtils.isEmpty(stepChapterIds)) {
            // 查询所有
            List<StepDTO> stepDTOS = stepService.listStep(chapterId);
            stepChapterIds = stepDTOS.stream().map(StepDTO::getStepChapterId).collect(Collectors.toList());
        }

        final List<Long> finalStepChapterIds = stepChapterIds;
        List<ResourcesParam> data = Lists.newArrayList();
        resourcesTypeEnums.forEach(resourcesTypeEnum -> {
            if ((resourcesTypeEnum == ResourcesTypeEnum.LABORATORY_MANUAL
                    || resourcesTypeEnum == ResourcesTypeEnum.EXPERIMENT_VIDEO)
                    && stepTypeEnum == StepTypeEnum.MULTIPLE) {
                finalStepChapterIds.forEach(stepChapterId -> {
                    ResourcesParam param = new ResourcesParam();
                    param.setResourceType(resourcesTypeEnum.getCode());
                    param.setObjId(stepChapterId);
                    param.setResourceRelationType(ResourceRelationTypeEnum.STEP.getCode());
                    if (param.getObjId() == null) {
                        throw new BadRequestException();
                    }
                    data.add(param);
                });
            } else {
                ResourcesParam param = new ResourcesParam();
                param.setResourceType(resourcesTypeEnum.getCode());
                param.setObjId(chapterId);
                param.setResourceRelationType(ResourceRelationTypeEnum.CHAPTER.getCode());
                data.add(param);
            }

        });
        return data;
    }

    private void checkStepChapterId(Long stepChapterId, Long chapterId) {
        if (stepChapterId == null) return;
        List<StepChapter> stepChapters = stepService.listStepChapter(Lists.newArrayList(chapterId));
        if (CollectionUtils.isEmpty(stepChapters) || stepChapters.stream()
                .filter(stepChapter -> Objects.equals(stepChapter.getId(), stepChapterId)).count() == 0) {
            throw new BadRequestException();
        }
    }

    private List<String> deleteStep(List<Long> dbChapterIds) {
        List<String> deleteFiles = Lists.newArrayList();
        dbChapterIds.forEach(chapterId -> {
            try {
                List<String> data = stepService.deleteStep(chapterId, null, false);
                deleteFiles.addAll(CollectionUtils.isEmpty(data) ? Lists.newArrayList() : data);
            } catch (NotFoundException e) {

            }
        });
        return deleteFiles;
    }

    private Map<Long, Long> copyStep(Map<Long, Long> chapterMap) {

        if (MapUtils.isEmpty(chapterMap)) return null;

        // 获取之前的步骤信息
        List<StepChapter> stepChapters = stepService.listStepChapter(Lists.newArrayList(chapterMap.keySet()));
        if (CollectionUtils.isEmpty(stepChapters)) return null;

        Map<Long, Long> stepMap = Maps.newHashMap();
        for (int i = 0; i < stepChapters.size(); i++) {
            StepChapter stepChapter = stepChapters.get(i);
            Long oldId = stepChapter.getId();
            Long newChapterId = chapterMap.get(stepChapter.getChapterId());
            if (newChapterId == null) continue;

            stepChapter.setId(null);
            stepChapter.setUpdateTime(null);
            stepChapter.setCreateTime(new Date());
            stepChapter.setChapterId(newChapterId);
            stepService.insertStepChapter(stepChapter);
            stepMap.put(oldId, stepChapter.getId());
        }

        // 插入resource_relation表
        insertResourceRelation(stepMap, null, ResourceRelationTypeEnum.STEP);

        return stepMap;

    }

    private Map<Long, Long> taskConvertChapter(Long courseId, List<ChapterBasicInfoDTO> taskInfos) {
        if (CollectionUtils.isEmpty(taskInfos)) return null;

        // 插入章节基本信息
        List<ChapterBasic> chapterBasics = Lists.newArrayList();
        taskInfos.forEach(chapterBasicInfoDTO -> {
            ChapterBasic chapterBasic = new ChapterBasic();
            chapterBasic.setName(chapterBasicInfoDTO.getName());
            chapterBasic.setType(ChapterTypeEnum.EXPERIMENT.getCode());
            chapterBasic.setStepType(chapterBasicInfoDTO.getStepType());
            chapterBasics.add(chapterBasic);
        });
        insertBatchChapterBasic(chapterBasics);

        // 查询最大的章节id，转换章节并插入章节。
        Integer orderNumber = getOrderNumber(courseId);
        List<Chapter> chapters = Lists.newArrayList();
        for (ChapterBasic chapterBasic : chapterBasics) {
            Chapter chapter = new Chapter();
            chapter.setCourseId(courseId);
            chapter.setChapterBasicId(chapterBasic.getId());
            chapter.setOrderNumber(orderNumber);
            chapters.add(chapter);
            orderNumber++;
        }
        insertBatchChapter(chapters);

        // 插入任务基本信息
        chapterBasics.forEach(chapterBasic -> chapterBasic.setId(null));
        insertBatchChapterBasic(chapterBasics);

        // 插入任务
        for (int i = 0; i < chapters.size(); i++) {
            Chapter chapter = chapters.get(i);
            chapter.setChapterBasicId(chapterBasics.get(i).getId());
            chapter.setParentId(chapter.getId());
            chapter.setId(null);
            chapter.setOrderNumber(1);
            chapter.setDifficultyScore(taskInfos.get(i).getDifficultyScore());
        }
        insertBatchChapter(chapters);

        Map<Long, Long> taskMap = Maps.newHashMap();
        for (int i = 0; i < taskInfos.size(); i++) {
            ChapterBasicInfoDTO chapterBasicInfoDTO = taskInfos.get(i);
            taskMap.put(chapterBasicInfoDTO.getId(), chapters.get(i).getId());
        }
        return taskMap;
    }
}
